xref: /llvm-project/llvm/utils/TableGen/DXILEmitter.cpp (revision 011b618644113996e2c0a8e57db40f89d20878e3)
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