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