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