1 //===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===// 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 // DXILEmitter uses the descriptions of DXIL operation to construct enum and 10 // helper functions for DXIL operation. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "Basic/SequenceToOffsetTable.h" 15 #include "Common/CodeGenTarget.h" 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/ADT/StringExtras.h" 19 #include "llvm/ADT/StringSet.h" 20 #include "llvm/ADT/StringSwitch.h" 21 #include "llvm/Support/DXILABI.h" 22 #include "llvm/Support/VersionTuple.h" 23 #include "llvm/TableGen/Error.h" 24 #include "llvm/TableGen/Record.h" 25 #include "llvm/TableGen/TableGenBackend.h" 26 27 #include <string> 28 #include <vector> 29 30 using namespace llvm; 31 using namespace llvm::dxil; 32 33 namespace { 34 35 struct DXILIntrinsicSelect { 36 StringRef Intrinsic; 37 SmallVector<const Record *> ArgSelectRecords; 38 }; 39 40 static StringRef StripIntrinArgSelectTypePrefix(StringRef Type) { 41 StringRef Prefix = "IntrinArgSelect_"; 42 if (!Type.starts_with(Prefix)) { 43 PrintFatalError("IntrinArgSelectType definintion must be prefixed with " 44 "'IntrinArgSelect_'"); 45 } 46 return Type.substr(Prefix.size()); 47 } 48 49 struct DXILOperationDesc { 50 std::string OpName; // name of DXIL operation 51 int OpCode; // ID of DXIL operation 52 StringRef OpClass; // name of the opcode class 53 StringRef Doc; // the documentation description of this instruction 54 // Vector of operand type records - return type is at index 0 55 SmallVector<const Record *> OpTypes; 56 SmallVector<const Record *> OverloadRecs; 57 SmallVector<const Record *> StageRecs; 58 SmallVector<const Record *> AttrRecs; 59 SmallVector<DXILIntrinsicSelect> IntrinsicSelects; 60 SmallVector<StringRef, 4> 61 ShaderStages; // shader stages to which this applies, empty for all. 62 int OverloadParamIndex; // Index of parameter with overload type. 63 // -1 : no overload types 64 SmallVector<StringRef, 4> Counters; // counters for this inst. 65 DXILOperationDesc(const Record *); 66 }; 67 } // end anonymous namespace 68 69 /// In-place sort TableGen records of class with a field 70 /// Version dxil_version 71 /// in the ascending version order. 72 static void ascendingSortByVersion(std::vector<const Record *> &Recs) { 73 sort(Recs, [](const Record *RecA, const Record *RecB) { 74 unsigned RecAMaj = 75 RecA->getValueAsDef("dxil_version")->getValueAsInt("Major"); 76 unsigned RecAMin = 77 RecA->getValueAsDef("dxil_version")->getValueAsInt("Minor"); 78 unsigned RecBMaj = 79 RecB->getValueAsDef("dxil_version")->getValueAsInt("Major"); 80 unsigned RecBMin = 81 RecB->getValueAsDef("dxil_version")->getValueAsInt("Minor"); 82 83 return (VersionTuple(RecAMaj, RecAMin) < VersionTuple(RecBMaj, RecBMin)); 84 }); 85 } 86 87 /// Take a `int_{intrinsic_name}` and return just the intrinsic_name part if 88 /// available. Otherwise return the empty string. 89 static StringRef GetIntrinsicName(const RecordVal *RV) { 90 if (RV && RV->getValue()) { 91 if (const DefInit *DI = dyn_cast<DefInit>(RV->getValue())) { 92 auto *IntrinsicDef = DI->getDef(); 93 auto DefName = IntrinsicDef->getName(); 94 assert(DefName.starts_with("int_") && "invalid intrinsic name"); 95 // Remove the int_ from intrinsic name. 96 return DefName.substr(4); 97 } 98 } 99 return ""; 100 } 101 102 /// Construct an object using the DXIL Operation records specified 103 /// in DXIL.td. This serves as the single source of reference of 104 /// the information extracted from the specified Record R, for 105 /// C++ code generated by this TableGen backend. 106 // \param R Object representing TableGen record of a DXIL Operation 107 DXILOperationDesc::DXILOperationDesc(const Record *R) { 108 OpName = R->getNameInitAsString(); 109 OpCode = R->getValueAsInt("OpCode"); 110 111 Doc = R->getValueAsString("Doc"); 112 SmallVector<const Record *> ParamTypeRecs; 113 114 ParamTypeRecs.push_back(R->getValueAsDef("result")); 115 116 for (const Record *ArgTy : R->getValueAsListOfDefs("arguments")) { 117 ParamTypeRecs.push_back(ArgTy); 118 } 119 size_t ParamTypeRecsSize = ParamTypeRecs.size(); 120 // Populate OpTypes with return type and parameter types 121 122 // Parameter indices of overloaded parameters. 123 // This vector contains overload parameters in the order used to 124 // resolve an LLVMMatchType in accordance with convention outlined in 125 // the comment before the definition of class LLVMMatchType in 126 // llvm/IR/Intrinsics.td 127 OverloadParamIndex = -1; // A sigil meaning none. 128 for (unsigned I = 0; I < ParamTypeRecsSize; I++) { 129 const Record *TR = ParamTypeRecs[I]; 130 // Track operation parameter indices of any overload types 131 if (TR->getValueAsInt("isOverload")) { 132 if (OverloadParamIndex != -1) { 133 assert(TR == ParamTypeRecs[OverloadParamIndex] && 134 "Specification of multiple differing overload parameter types " 135 "is not supported"); 136 } 137 // Keep the earliest parameter index we see, but if it was the return type 138 // overwrite it with the first overloaded argument. 139 if (OverloadParamIndex <= 0) 140 OverloadParamIndex = I; 141 } 142 OpTypes.emplace_back(TR); 143 } 144 145 // Get overload records 146 std::vector<const Record *> Recs = R->getValueAsListOfDefs("overloads"); 147 148 // Sort records in ascending order of DXIL version 149 ascendingSortByVersion(Recs); 150 151 for (const Record *CR : Recs) { 152 OverloadRecs.push_back(CR); 153 } 154 155 // Get stage records 156 Recs = R->getValueAsListOfDefs("stages"); 157 158 if (Recs.empty()) { 159 PrintFatalError(R, Twine("Atleast one specification of valid stage for ") + 160 OpName + " is required"); 161 } 162 163 // Sort records in ascending order of DXIL version 164 ascendingSortByVersion(Recs); 165 166 for (const Record *CR : Recs) { 167 StageRecs.push_back(CR); 168 } 169 170 // Get attribute records 171 Recs = R->getValueAsListOfDefs("attributes"); 172 173 // Sort records in ascending order of DXIL version 174 ascendingSortByVersion(Recs); 175 176 for (const Record *CR : Recs) { 177 AttrRecs.push_back(CR); 178 } 179 180 // Get the operation class 181 OpClass = R->getValueAsDef("OpClass")->getName(); 182 183 if (!OpClass.str().compare("UnknownOpClass")) { 184 PrintFatalError(R, Twine("Unspecified DXIL OpClass for DXIL operation - ") + 185 OpName); 186 } 187 188 auto IntrinsicSelectRecords = R->getValueAsListOfDefs("intrinsics"); 189 if (IntrinsicSelectRecords.size()) { 190 for (const Record *R : IntrinsicSelectRecords) { 191 DXILIntrinsicSelect IntrSelect; 192 IntrSelect.Intrinsic = GetIntrinsicName(R->getValue("intrinsic")); 193 auto Args = R->getValueAsListOfDefs("arg_selects"); 194 for (const Record *ArgSelect : Args) { 195 IntrSelect.ArgSelectRecords.emplace_back(ArgSelect); 196 } 197 IntrinsicSelects.emplace_back(std::move(IntrSelect)); 198 } 199 } 200 } 201 202 /// Return a string representation of OverloadKind enum that maps to 203 /// input LLVMType record 204 /// \param R TableGen def record of class LLVMType 205 /// \return std::string string representation of OverloadKind 206 207 static StringRef getOverloadKindStr(const Record *R) { 208 // TODO: This is a hack. We need to rework how we're handling the set of 209 // overloads to avoid this business with the separate OverloadKind enum. 210 return StringSwitch<StringRef>(R->getName()) 211 .Case("HalfTy", "OverloadKind::HALF") 212 .Case("FloatTy", "OverloadKind::FLOAT") 213 .Case("DoubleTy", "OverloadKind::DOUBLE") 214 .Case("Int1Ty", "OverloadKind::I1") 215 .Case("Int8Ty", "OverloadKind::I8") 216 .Case("Int16Ty", "OverloadKind::I16") 217 .Case("Int32Ty", "OverloadKind::I32") 218 .Case("Int64Ty", "OverloadKind::I64") 219 .Case("ResRetHalfTy", "OverloadKind::HALF") 220 .Case("ResRetFloatTy", "OverloadKind::FLOAT") 221 .Case("ResRetDoubleTy", "OverloadKind::DOUBLE") 222 .Case("ResRetInt16Ty", "OverloadKind::I16") 223 .Case("ResRetInt32Ty", "OverloadKind::I32") 224 .Case("ResRetInt64Ty", "OverloadKind::I64"); 225 } 226 227 /// Return a string representation of valid overload information denoted 228 // by input records 229 // 230 /// \param Recs A vector of records of TableGen Overload records 231 /// \return std::string string representation of overload mask string 232 /// predicated by DXIL Version. E.g., 233 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...} 234 static std::string getOverloadMaskString(ArrayRef<const Record *> Recs) { 235 std::string MaskString = ""; 236 std::string Prefix = ""; 237 MaskString.append("{"); 238 // If no overload information records were specified, assume the operation 239 // a) to be supported in DXIL Version 1.0 and later 240 // b) has no overload types 241 if (Recs.empty()) { 242 MaskString.append("{{1, 0}, OverloadKind::UNDEFINED}}"); 243 } else { 244 for (const auto *Rec : Recs) { 245 unsigned Major = 246 Rec->getValueAsDef("dxil_version")->getValueAsInt("Major"); 247 unsigned Minor = 248 Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor"); 249 MaskString.append(Prefix) 250 .append("{{") 251 .append(std::to_string(Major)) 252 .append(", ") 253 .append(std::to_string(Minor).append("}, ")); 254 255 std::string PipePrefix = ""; 256 auto Tys = Rec->getValueAsListOfDefs("overload_types"); 257 if (Tys.empty()) { 258 MaskString.append("OverloadKind::UNDEFINED"); 259 } 260 for (const auto *Ty : Tys) { 261 MaskString.append(PipePrefix).append(getOverloadKindStr(Ty)); 262 PipePrefix = " | "; 263 } 264 265 MaskString.append("}"); 266 Prefix = ", "; 267 } 268 MaskString.append("}"); 269 } 270 return MaskString; 271 } 272 273 /// Return a string representation of valid shader stag information denoted 274 // by input records 275 // 276 /// \param Recs A vector of records of TableGen Stages records 277 /// \return std::string string representation of stages mask string 278 /// predicated by DXIL Version. E.g., 279 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...} 280 static std::string getStageMaskString(ArrayRef<const Record *> Recs) { 281 std::string MaskString = ""; 282 std::string Prefix = ""; 283 MaskString.append("{"); 284 // Atleast one stage information record is expected to be specified. 285 if (Recs.empty()) { 286 PrintFatalError("Atleast one specification of valid stages for " 287 "operation must be specified"); 288 } 289 290 for (const auto *Rec : Recs) { 291 unsigned Major = Rec->getValueAsDef("dxil_version")->getValueAsInt("Major"); 292 unsigned Minor = Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor"); 293 MaskString.append(Prefix) 294 .append("{{") 295 .append(std::to_string(Major)) 296 .append(", ") 297 .append(std::to_string(Minor).append("}, ")); 298 299 std::string PipePrefix = ""; 300 auto Stages = Rec->getValueAsListOfDefs("shader_stages"); 301 if (Stages.empty()) { 302 PrintFatalError("No valid stages for operation specified"); 303 } 304 for (const auto *S : Stages) { 305 MaskString.append(PipePrefix).append("ShaderKind::").append(S->getName()); 306 PipePrefix = " | "; 307 } 308 309 MaskString.append("}"); 310 Prefix = ", "; 311 } 312 MaskString.append("}"); 313 return MaskString; 314 } 315 316 /// Return a string representation of valid attribute information denoted 317 // by input records 318 // 319 /// \param Recs A vector of records of TableGen Attribute records 320 /// \return std::string string representation of stages mask string 321 /// predicated by DXIL Version. E.g., 322 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...} 323 static std::string getAttributeMaskString(ArrayRef<const Record *> Recs) { 324 std::string MaskString = ""; 325 std::string Prefix = ""; 326 MaskString.append("{"); 327 328 for (const auto *Rec : Recs) { 329 unsigned Major = Rec->getValueAsDef("dxil_version")->getValueAsInt("Major"); 330 unsigned Minor = Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor"); 331 MaskString.append(Prefix) 332 .append("{{") 333 .append(std::to_string(Major)) 334 .append(", ") 335 .append(std::to_string(Minor).append("}, ")); 336 337 std::string PipePrefix = ""; 338 auto Attrs = Rec->getValueAsListOfDefs("op_attrs"); 339 if (Attrs.empty()) { 340 MaskString.append("Attribute::None"); 341 } else { 342 for (const auto *Attr : Attrs) { 343 MaskString.append(PipePrefix) 344 .append("Attribute::") 345 .append(Attr->getName()); 346 PipePrefix = " | "; 347 } 348 } 349 350 MaskString.append("}"); 351 Prefix = ", "; 352 } 353 MaskString.append("}"); 354 return MaskString; 355 } 356 357 /// Emit a mapping of DXIL opcode to opname 358 static void emitDXILOpCodes(ArrayRef<DXILOperationDesc> Ops, raw_ostream &OS) { 359 OS << "#ifdef DXIL_OPCODE\n"; 360 for (const DXILOperationDesc &Op : Ops) 361 OS << "DXIL_OPCODE(" << Op.OpCode << ", " << Op.OpName << ")\n"; 362 OS << "#undef DXIL_OPCODE\n"; 363 OS << "\n"; 364 OS << "#endif\n\n"; 365 } 366 367 /// Emit a list of DXIL op classes 368 static void emitDXILOpClasses(const RecordKeeper &Records, raw_ostream &OS) { 369 OS << "#ifdef DXIL_OPCLASS\n"; 370 for (const Record *OpClass : Records.getAllDerivedDefinitions("DXILOpClass")) 371 OS << "DXIL_OPCLASS(" << OpClass->getName() << ")\n"; 372 OS << "#undef DXIL_OPCLASS\n"; 373 OS << "#endif\n\n"; 374 } 375 376 /// Emit a list of DXIL op parameter types 377 static void emitDXILOpParamTypes(const RecordKeeper &Records, raw_ostream &OS) { 378 OS << "#ifdef DXIL_OP_PARAM_TYPE\n"; 379 for (const Record *OpParamType : 380 Records.getAllDerivedDefinitions("DXILOpParamType")) 381 OS << "DXIL_OP_PARAM_TYPE(" << OpParamType->getName() << ")\n"; 382 OS << "#undef DXIL_OP_PARAM_TYPE\n"; 383 OS << "#endif\n\n"; 384 } 385 386 /// Emit a list of DXIL op function types 387 static void emitDXILOpFunctionTypes(ArrayRef<DXILOperationDesc> Ops, 388 raw_ostream &OS) { 389 OS << "#ifndef DXIL_OP_FUNCTION_TYPE\n"; 390 OS << "#define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...)\n"; 391 OS << "#endif\n"; 392 for (const DXILOperationDesc &Op : Ops) { 393 OS << "DXIL_OP_FUNCTION_TYPE(dxil::OpCode::" << Op.OpName; 394 for (const Record *Rec : Op.OpTypes) 395 OS << ", dxil::OpParamType::" << Rec->getName(); 396 // If there are no arguments, we need an empty comma for the varargs 397 if (Op.OpTypes.size() == 1) 398 OS << ", "; 399 OS << ")\n"; 400 } 401 OS << "#undef DXIL_OP_FUNCTION_TYPE\n"; 402 } 403 404 /// Emit map of DXIL operation to LLVM or DirectX intrinsic 405 /// \param A vector of DXIL Ops 406 /// \param Output stream 407 static void emitDXILIntrinsicMap(ArrayRef<DXILOperationDesc> Ops, 408 raw_ostream &OS) { 409 410 OS << "#ifdef DXIL_OP_INTRINSIC\n"; 411 OS << "\n"; 412 for (const auto &Op : Ops) { 413 if (Op.IntrinsicSelects.empty()) { 414 continue; 415 } 416 for (const DXILIntrinsicSelect &MappedIntr : Op.IntrinsicSelects) { 417 OS << "DXIL_OP_INTRINSIC(dxil::OpCode::" << Op.OpName 418 << ", Intrinsic::" << MappedIntr.Intrinsic << ", "; 419 for (const Record *ArgSelect : MappedIntr.ArgSelectRecords) { 420 std::string Type = 421 ArgSelect->getValueAsDef("type")->getNameInitAsString(); 422 int Value = ArgSelect->getValueAsInt("value"); 423 OS << "(IntrinArgSelect{" 424 << "IntrinArgSelect::Type::" << StripIntrinArgSelectTypePrefix(Type) 425 << "," << Value << "}), "; 426 } 427 OS << ")\n"; 428 } 429 } 430 OS << "\n"; 431 OS << "#undef DXIL_OP_INTRINSIC\n"; 432 OS << "#endif\n\n"; 433 } 434 435 /// Emit the IntrinArgSelect type for DirectX intrinsic to DXIL Op lowering 436 static void emitDXILIntrinsicArgSelectTypes(const RecordKeeper &Records, 437 raw_ostream &OS) { 438 OS << "#ifdef DXIL_OP_INTRINSIC_ARG_SELECT_TYPE\n"; 439 for (const Record *Records : 440 Records.getAllDerivedDefinitions("IntrinArgSelectType")) { 441 StringRef StrippedName = StripIntrinArgSelectTypePrefix(Records->getName()); 442 OS << "DXIL_OP_INTRINSIC_ARG_SELECT_TYPE(" << StrippedName << ")\n"; 443 } 444 OS << "#undef DXIL_OP_INTRINSIC_ARG_SELECT_TYPE\n"; 445 OS << "#endif\n\n"; 446 } 447 448 /// Emit DXIL operation table 449 /// \param A vector of DXIL Ops 450 /// \param Output stream 451 static void emitDXILOperationTable(ArrayRef<DXILOperationDesc> Ops, 452 raw_ostream &OS) { 453 // Collect Names. 454 SequenceToOffsetTable<std::string> OpClassStrings; 455 SequenceToOffsetTable<std::string> OpStrings; 456 457 StringSet<> ClassSet; 458 for (const auto &Op : Ops) { 459 OpStrings.add(Op.OpName); 460 461 if (ClassSet.insert(Op.OpClass).second) 462 OpClassStrings.add(Op.OpClass.data()); 463 } 464 465 // Layout names. 466 OpStrings.layout(); 467 OpClassStrings.layout(); 468 469 // Emit access function getOpcodeProperty() that embeds DXIL Operation table 470 // with entries of type struct OpcodeProperty. 471 OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) " 472 "{\n"; 473 474 OS << " static const OpCodeProperty OpCodeProps[] = {\n"; 475 std::string Prefix = ""; 476 for (const auto &Op : Ops) { 477 OS << Prefix << " { dxil::OpCode::" << Op.OpName << ", " 478 << OpStrings.get(Op.OpName) << ", OpCodeClass::" << Op.OpClass << ", " 479 << OpClassStrings.get(Op.OpClass.data()) << ", " 480 << getOverloadMaskString(Op.OverloadRecs) << ", " 481 << getStageMaskString(Op.StageRecs) << ", " 482 << getAttributeMaskString(Op.AttrRecs) << ", " << Op.OverloadParamIndex 483 << " }"; 484 Prefix = ",\n"; 485 } 486 OS << " };\n"; 487 488 OS << " // FIXME: change search to indexing with\n"; 489 OS << " // Op once all DXIL operations are added.\n"; 490 OS << " OpCodeProperty TmpProp;\n"; 491 OS << " TmpProp.OpCode = Op;\n"; 492 OS << " const OpCodeProperty *Prop =\n"; 493 OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n"; 494 OS << " [](const OpCodeProperty &A, const " 495 "OpCodeProperty &B) {\n"; 496 OS << " return A.OpCode < B.OpCode;\n"; 497 OS << " });\n"; 498 OS << " assert(Prop && \"failed to find OpCodeProperty\");\n"; 499 OS << " return Prop;\n"; 500 OS << "}\n\n"; 501 502 // Emit the string tables. 503 OS << "static const char *getOpCodeName(dxil::OpCode Op) {\n\n"; 504 505 OpStrings.emitStringLiteralDef(OS, 506 " static const char DXILOpCodeNameTable[]"); 507 508 OS << " auto *Prop = getOpCodeProperty(Op);\n"; 509 OS << " unsigned Index = Prop->OpCodeNameOffset;\n"; 510 OS << " return DXILOpCodeNameTable + Index;\n"; 511 OS << "}\n\n"; 512 513 OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) " 514 "{\n\n"; 515 516 OpClassStrings.emitStringLiteralDef( 517 OS, " static const char DXILOpCodeClassNameTable[]"); 518 519 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n"; 520 OS << " return DXILOpCodeClassNameTable + Index;\n"; 521 OS << "}\n\n"; 522 } 523 524 static void emitDXILOperationTableDataStructs(const RecordKeeper &Records, 525 raw_ostream &OS) { 526 // Get Shader stage records 527 std::vector<const Record *> ShaderKindRecs = 528 Records.getAllDerivedDefinitions("DXILShaderStage"); 529 // Sort records by name 530 llvm::sort(ShaderKindRecs, [](const Record *A, const Record *B) { 531 return A->getName() < B->getName(); 532 }); 533 534 OS << "// Valid shader kinds\n\n"; 535 // Choose the type of enum ShaderKind based on the number of stages declared. 536 // This gives the flexibility to just add add new stage records in DXIL.td, if 537 // needed, with no need to change this backend code. 538 size_t ShaderKindCount = ShaderKindRecs.size(); 539 uint64_t ShaderKindTySz = PowerOf2Ceil(ShaderKindRecs.size() + 1); 540 OS << "enum ShaderKind : uint" << ShaderKindTySz << "_t {\n"; 541 const std::string AllStages("all_stages"); 542 const std::string Removed("removed"); 543 int ShiftVal = 1; 544 for (const auto *R : ShaderKindRecs) { 545 auto Name = R->getName(); 546 if (Name.compare(Removed) == 0) { 547 OS << " " << Name 548 << " = 0, // Pseudo-stage indicating op not supported in any " 549 "stage\n"; 550 } else if (Name.compare(AllStages) == 0) { 551 OS << " " << Name << " = 0x" 552 << utohexstr(((1 << ShaderKindCount) - 1), false, 0) 553 << ", // Pseudo-stage indicating op is supported in all stages\n"; 554 } else if (Name.compare(AllStages)) { 555 OS << " " << Name << " = 1 << " << std::to_string(ShiftVal++) << ",\n"; 556 } 557 } 558 OS << "}; // enum ShaderKind\n\n"; 559 } 560 561 /// Entry function call that invokes the functionality of this TableGen backend 562 /// \param Records TableGen records of DXIL Operations defined in DXIL.td 563 /// \param OS output stream 564 static void emitDxilOperation(const RecordKeeper &Records, raw_ostream &OS) { 565 OS << "// Generated code, do not edit.\n"; 566 OS << "\n"; 567 // Get all DXIL Ops property records 568 std::vector<DXILOperationDesc> DXILOps; 569 for (const Record *R : Records.getAllDerivedDefinitions("DXILOp")) { 570 DXILOps.emplace_back(DXILOperationDesc(R)); 571 } 572 // Sort by opcode. 573 llvm::sort(DXILOps, 574 [](const DXILOperationDesc &A, const DXILOperationDesc &B) { 575 return A.OpCode < B.OpCode; 576 }); 577 int PrevOp = -1; 578 for (const DXILOperationDesc &Desc : DXILOps) { 579 if (Desc.OpCode == PrevOp) 580 PrintFatalError(Twine("Duplicate opcode: ") + Twine(Desc.OpCode)); 581 PrevOp = Desc.OpCode; 582 } 583 584 emitDXILOpCodes(DXILOps, OS); 585 emitDXILOpClasses(Records, OS); 586 emitDXILOpParamTypes(Records, OS); 587 emitDXILOpFunctionTypes(DXILOps, OS); 588 emitDXILIntrinsicArgSelectTypes(Records, OS); 589 emitDXILIntrinsicMap(DXILOps, OS); 590 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n\n"; 591 emitDXILOperationTableDataStructs(Records, OS); 592 emitDXILOperationTable(DXILOps, OS); 593 OS << "#undef DXIL_OP_OPERATION_TABLE\n"; 594 OS << "#endif\n\n"; 595 } 596 597 static TableGen::Emitter::Opt X("gen-dxil-operation", emitDxilOperation, 598 "Generate DXIL operation information"); 599