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