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