Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
213765 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Basic/SequenceToOffsetTable.h"9#include "Common/CodeGenDAGPatterns.h" // For SDNodeInfo.10#include "llvm/Support/CommandLine.h"11#include "llvm/Support/FormatVariadic.h"12#include "llvm/TableGen/Error.h"13#include "llvm/TableGen/StringToOffsetTable.h"14#include "llvm/TableGen/TableGenBackend.h"1516using namespace llvm;1718static cl::OptionCategory SDNodeInfoEmitterCat("Options for -gen-sdnode-info");1920static cl::opt<std::string> TargetSDNodeNamespace(21"sdnode-namespace", cl::cat(SDNodeInfoEmitterCat),22cl::desc("Specify target SDNode namespace (default=<Target>ISD)"));2324static cl::opt<bool> WarnOnSkippedNodes(25"warn-on-skipped-nodes", cl::cat(SDNodeInfoEmitterCat),26cl::desc("Explain why a node was skipped (default=true)"), cl::init(true));2728namespace {2930class SDNodeInfoEmitter {31const RecordKeeper &RK;32const CodeGenTarget Target;33std::map<StringRef, SmallVector<SDNodeInfo, 2>> NodesByName;3435public:36explicit SDNodeInfoEmitter(const RecordKeeper &RK);3738void run(raw_ostream &OS) const;3940private:41void emitEnum(raw_ostream &OS) const;4243std::vector<unsigned> emitNodeNames(raw_ostream &OS) const;4445std::vector<std::pair<unsigned, unsigned>>46emitTypeConstraints(raw_ostream &OS) const;4748void emitDescs(raw_ostream &OS) const;49};5051} // namespace5253static bool haveCompatibleDescriptions(const SDNodeInfo &N1,54const SDNodeInfo &N2) {55// Number of results/operands must match.56if (N1.getNumResults() != N2.getNumResults() ||57N1.getNumOperands() != N2.getNumOperands())58return false;5960// Flags must match.61if (N1.isStrictFP() != N2.isStrictFP() || N1.getTSFlags() != N2.getTSFlags())62return false;6364// We're only interested in a subset of node properties. Properties like65// SDNPAssociative and SDNPCommutative do not impose constraints on nodes,66// and sometimes differ between nodes sharing the same enum name.67constexpr unsigned PropMask = (1 << SDNPHasChain) | (1 << SDNPOutGlue) |68(1 << SDNPInGlue) | (1 << SDNPOptInGlue) |69(1 << SDNPMemOperand) | (1 << SDNPVariadic);7071return (N1.getProperties() & PropMask) == (N2.getProperties() & PropMask);72}7374static bool haveCompatibleDescriptions(ArrayRef<SDNodeInfo> Nodes) {75const SDNodeInfo &N = Nodes.front();76return all_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {77return haveCompatibleDescriptions(Other, N);78});79}8081static void warnOnSkippedNode(const SDNodeInfo &N, const Twine &Reason) {82PrintWarning(N.getRecord()->getLoc(), "skipped node: " + Reason);83}8485SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)86: RK(RK), Target(RK) {87const CodeGenHwModes &HwModes = Target.getHwModes();8889// Figure out target SDNode namespace.90if (!TargetSDNodeNamespace.getNumOccurrences())91TargetSDNodeNamespace = Target.getName().str() + "ISD";9293// Filter nodes by the target SDNode namespace and create a mapping94// 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.96for (const Record *R : RK.getAllDerivedDefinitions("SDNode")) {97SDNodeInfo Node(R, HwModes);98auto [NS, EnumName] = Node.getEnumName().split("::");99100if (NS.empty() || EnumName.empty()) {101if (WarnOnSkippedNodes)102warnOnSkippedNode(Node, "invalid enum name");103continue;104}105106if (NS != TargetSDNodeNamespace)107continue;108109NodesByName[EnumName].push_back(std::move(Node));110}111112// Filter out nodes that have different "prototypes" and/or flags.113// Don't look at type constraints though, we will simply skip emitting114// the constraints if they differ.115decltype(NodesByName)::iterator Next;116for (auto I = NodesByName.begin(), E = NodesByName.end(); I != E; I = Next) {117Next = std::next(I);118119if (haveCompatibleDescriptions(I->second))120continue;121122if (WarnOnSkippedNodes)123for (const SDNodeInfo &N : I->second)124warnOnSkippedNode(N, "incompatible description");125126NodesByName.erase(I);127}128}129130void SDNodeInfoEmitter::emitEnum(raw_ostream &OS) const {131OS << "#ifdef GET_SDNODE_ENUM\n";132OS << "#undef GET_SDNODE_ENUM\n\n";133OS << "namespace llvm::" << TargetSDNodeNamespace << " {\n\n";134135if (!NodesByName.empty()) {136StringRef FirstName = NodesByName.begin()->first;137StringRef LastName = NodesByName.rbegin()->first;138139OS << "enum GenNodeType : unsigned {\n";140OS << " " << FirstName << " = ISD::BUILTIN_OP_END,\n";141142for (StringRef EnumName : make_first_range(drop_begin(NodesByName)))143OS << " " << EnumName << ",\n";144145OS << "};\n\n";146OS << "static constexpr unsigned GENERATED_OPCODE_END = " << LastName147<< " + 1;\n\n";148} else {149OS << "static constexpr unsigned GENERATED_OPCODE_END = "150"ISD::BUILTIN_OP_END;\n\n";151}152153OS << "} // namespace llvm::" << TargetSDNodeNamespace << "\n\n";154OS << "#endif // GET_SDNODE_ENUM\n\n";155}156157std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {158StringToOffsetTable NameTable;159160std::vector<unsigned> NameOffsets;161NameOffsets.reserve(NodesByName.size());162163for (StringRef EnumName : make_first_range(NodesByName)) {164SmallString<64> DebugName;165raw_svector_ostream SS(DebugName);166SS << TargetSDNodeNamespace << "::" << EnumName;167NameOffsets.push_back(NameTable.GetOrAddStringOffset(DebugName));168}169170NameTable.EmitStringTableDef(OS, Target.getName() + "SDNodeNames");171OS << '\n';172173return NameOffsets;174}175176static StringRef getTypeConstraintKindName(SDTypeConstraint::KindTy Kind) {177#define CASE(NAME) \178case SDTypeConstraint::NAME: \179return #NAME180181switch (Kind) {182CASE(SDTCisVT);183CASE(SDTCisPtrTy);184CASE(SDTCisInt);185CASE(SDTCisFP);186CASE(SDTCisVec);187CASE(SDTCisSameAs);188CASE(SDTCisVTSmallerThanOp);189CASE(SDTCisOpSmallerThanOp);190CASE(SDTCisEltOfVec);191CASE(SDTCisSubVecOfVec);192CASE(SDTCVecEltisVT);193CASE(SDTCisSameNumEltsAs);194CASE(SDTCisSameSizeAs);195}196llvm_unreachable("Unknown constraint kind"); // Make MSVC happy.197#undef CASE198}199200static void emitTypeConstraint(raw_ostream &OS, SDTypeConstraint C) {201unsigned OtherOpNo = 0;202MVT VT;203204switch (C.ConstraintType) {205case SDTypeConstraint::SDTCisVT:206case SDTypeConstraint::SDTCVecEltisVT:207if (C.VVT.isSimple())208VT = C.VVT.getSimple();209break;210case SDTypeConstraint::SDTCisPtrTy:211case SDTypeConstraint::SDTCisInt:212case SDTypeConstraint::SDTCisFP:213case SDTypeConstraint::SDTCisVec:214break;215case SDTypeConstraint::SDTCisSameAs:216case SDTypeConstraint::SDTCisVTSmallerThanOp:217case SDTypeConstraint::SDTCisOpSmallerThanOp:218case SDTypeConstraint::SDTCisEltOfVec:219case SDTypeConstraint::SDTCisSubVecOfVec:220case SDTypeConstraint::SDTCisSameNumEltsAs:221case SDTypeConstraint::SDTCisSameSizeAs:222OtherOpNo = C.OtherOperandNo;223break;224}225226StringRef KindName = getTypeConstraintKindName(C.ConstraintType);227StringRef VTName = VT.SimpleTy == MVT::INVALID_SIMPLE_VALUE_TYPE228? "MVT::INVALID_SIMPLE_VALUE_TYPE"229: getEnumName(VT.SimpleTy);230OS << formatv("{{{}, {}, {}, {}}", KindName, C.OperandNo, OtherOpNo, VTName);231}232233std::vector<std::pair<unsigned, unsigned>>234SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {235using ConstraintsVecTy = SmallVector<SDTypeConstraint, 0>;236SequenceToOffsetTable<ConstraintsVecTy> ConstraintTable(237/*Terminator=*/std::nullopt);238239std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts;240ConstraintOffsetsAndCounts.reserve(NodesByName.size());241242SmallVector<StringRef> SkippedNodes;243for (const auto &[EnumName, Nodes] : NodesByName) {244ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();245246bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {247return ArrayRef(Other.getTypeConstraints()) != Constraints;248});249250// If nodes with the same enum name have different constraints,251// treat them as if they had no constraints at all.252if (IsAmbiguous) {253SkippedNodes.push_back(EnumName);254continue;255}256257// Don't add empty sequences to the table. This slightly simplifies258// the implementation and makes the output less confusing if the table259// ends up empty.260if (Constraints.empty())261continue;262263// SequenceToOffsetTable reuses the storage if a sequence matches another264// 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.266ConstraintTable.add(ConstraintsVecTy(reverse(Constraints)));267}268269ConstraintTable.layout();270271OS << "static const SDTypeConstraint " << Target.getName()272<< "SDTypeConstraints[] = {\n";273ConstraintTable.emit(OS, emitTypeConstraint);274OS << "};\n\n";275276for (const auto &[EnumName, Nodes] : NodesByName) {277ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();278279if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {280ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/0, /*Size=*/0);281continue;282}283284unsigned ConstraintsOffset =285ConstraintTable.get(ConstraintsVecTy(reverse(Constraints)));286ConstraintOffsetsAndCounts.emplace_back(ConstraintsOffset,287Constraints.size());288}289290return ConstraintOffsetsAndCounts;291}292293static void emitDesc(raw_ostream &OS, StringRef EnumName,294ArrayRef<SDNodeInfo> Nodes, unsigned NameOffset,295unsigned ConstraintsOffset, unsigned ConstraintCount) {296assert(haveCompatibleDescriptions(Nodes));297const SDNodeInfo &N = Nodes.front();298OS << " {" << N.getNumResults() << ", " << N.getNumOperands() << ", 0";299300// Emitted properties must be kept in sync with haveCompatibleDescriptions.301unsigned Properties = N.getProperties();302if (Properties & (1 << SDNPHasChain))303OS << "|1<<SDNPHasChain";304if (Properties & (1 << SDNPOutGlue))305OS << "|1<<SDNPOutGlue";306if (Properties & (1 << SDNPInGlue))307OS << "|1<<SDNPInGlue";308if (Properties & (1 << SDNPOptInGlue))309OS << "|1<<SDNPOptInGlue";310if (Properties & (1 << SDNPVariadic))311OS << "|1<<SDNPVariadic";312if (Properties & (1 << SDNPMemOperand))313OS << "|1<<SDNPMemOperand";314315OS << ", 0";316if (N.isStrictFP())317OS << "|1<<SDNFIsStrictFP";318319OS << formatv(", {}, {}, {}, {}}, // {}\n", N.getTSFlags(), NameOffset,320ConstraintsOffset, ConstraintCount, EnumName);321}322323void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {324StringRef TargetName = Target.getName();325326OS << "#ifdef GET_SDNODE_DESC\n";327OS << "#undef GET_SDNODE_DESC\n\n";328OS << "namespace llvm {\n";329330std::vector<unsigned> NameOffsets = emitNodeNames(OS);331std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts =332emitTypeConstraints(OS);333334OS << "static const SDNodeDesc " << TargetName << "SDNodeDescs[] = {\n";335336for (const auto &[NameAndNodes, NameOffset, ConstraintOffsetAndCount] :337zip_equal(NodesByName, NameOffsets, ConstraintOffsetsAndCounts))338emitDesc(OS, NameAndNodes.first, NameAndNodes.second, NameOffset,339ConstraintOffsetAndCount.first, ConstraintOffsetAndCount.second);340341OS << "};\n\n";342343OS << formatv("static const SDNodeInfo {0}GenSDNodeInfo(\n"344" /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"345" {0}SDNodeNames, {0}SDTypeConstraints);\n\n",346TargetName, NodesByName.size());347348OS << "} // namespace llvm\n\n";349OS << "#endif // GET_SDNODE_DESC\n\n";350}351352void SDNodeInfoEmitter::run(raw_ostream &OS) const {353emitSourceFileHeader("Target SDNode descriptions", OS, RK);354emitEnum(OS);355emitDescs(OS);356}357358static TableGen::Emitter::OptClass<SDNodeInfoEmitter>359X("gen-sd-node-info", "Generate target SDNode descriptions");360361362