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