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
30using namespace llvm;
31using namespace llvm::dxil;
32
33namespace {
34
35struct DXILIntrinsicSelect {
36 StringRef Intrinsic;
37 SmallVector<const Record *> ArgSelectRecords;
38};
39
40static StringRef StripIntrinArgSelectTypePrefix(StringRef Type) {
41 StringRef Prefix = "IntrinArgSelect_";
42 if (!Type.starts_with(Prefix)) {
43 PrintFatalError(Msg: "IntrinArgSelectType definintion must be prefixed with "
44 "'IntrinArgSelect_'");
45 }
46 return Type.substr(Start: Prefix.size());
47}
48
49struct 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.
72static void ascendingSortByVersion(std::vector<const Record *> &Recs) {
73 sort(C&: Recs, Comp: [](const Record *RecA, const Record *RecB) {
74 unsigned RecAMaj =
75 RecA->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Major");
76 unsigned RecAMin =
77 RecA->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Minor");
78 unsigned RecBMaj =
79 RecB->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Major");
80 unsigned RecBMin =
81 RecB->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "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.
89static StringRef GetIntrinsicName(const RecordVal *RV) {
90 if (RV && RV->getValue()) {
91 if (const DefInit *DI = dyn_cast<DefInit>(Val: 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(Start: 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
107DXILOperationDesc::DXILOperationDesc(const Record *R) {
108 OpName = R->getNameInitAsString();
109 OpCode = R->getValueAsInt(FieldName: "OpCode");
110
111 Doc = R->getValueAsString(FieldName: "Doc");
112 SmallVector<const Record *> ParamTypeRecs;
113
114 ParamTypeRecs.push_back(Elt: R->getValueAsDef(FieldName: "result"));
115
116 llvm::append_range(C&: ParamTypeRecs, R: R->getValueAsListOfDefs(FieldName: "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(FieldName: "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(Args&: TR);
141 }
142
143 // Get overload records
144 std::vector<const Record *> Recs = R->getValueAsListOfDefs(FieldName: "overloads");
145
146 // Sort records in ascending order of DXIL version
147 ascendingSortByVersion(Recs);
148
149 llvm::append_range(C&: OverloadRecs, R&: Recs);
150
151 // Get stage records
152 Recs = R->getValueAsListOfDefs(FieldName: "stages");
153
154 if (Recs.empty()) {
155 PrintFatalError(Rec: R, Msg: 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(C&: StageRecs, R&: Recs);
163
164 // Get attribute records
165 Recs = R->getValueAsListOfDefs(FieldName: "attributes");
166
167 // Sort records in ascending order of DXIL version
168 ascendingSortByVersion(Recs);
169
170 llvm::append_range(C&: AttrRecs, R&: Recs);
171
172 // Get the operation class
173 OpClass = R->getValueAsDef(FieldName: "OpClass")->getName();
174
175 if (OpClass.str() == "UnknownOpClass")
176 PrintFatalError(Rec: R, Msg: Twine("Unspecified DXIL OpClass for DXIL operation - ") +
177 OpName);
178
179 auto IntrinsicSelectRecords = R->getValueAsListOfDefs(FieldName: "intrinsics");
180 if (IntrinsicSelectRecords.size()) {
181 for (const Record *R : IntrinsicSelectRecords) {
182 DXILIntrinsicSelect IntrSelect;
183 IntrSelect.Intrinsic = GetIntrinsicName(RV: R->getValue(Name: "intrinsic"));
184 auto Args = R->getValueAsListOfDefs(FieldName: "arg_selects");
185 for (const Record *ArgSelect : Args) {
186 IntrSelect.ArgSelectRecords.emplace_back(Args&: ArgSelect);
187 }
188 IntrinsicSelects.emplace_back(Args: 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
198static 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(S: "HalfTy", Value: "OverloadKind::HALF")
203 .Case(S: "FloatTy", Value: "OverloadKind::FLOAT")
204 .Case(S: "DoubleTy", Value: "OverloadKind::DOUBLE")
205 .Case(S: "Int1Ty", Value: "OverloadKind::I1")
206 .Case(S: "Int8Ty", Value: "OverloadKind::I8")
207 .Case(S: "Int16Ty", Value: "OverloadKind::I16")
208 .Case(S: "Int32Ty", Value: "OverloadKind::I32")
209 .Case(S: "Int64Ty", Value: "OverloadKind::I64")
210 .Case(S: "ResRetHalfTy", Value: "OverloadKind::HALF")
211 .Case(S: "ResRetFloatTy", Value: "OverloadKind::FLOAT")
212 .Case(S: "ResRetDoubleTy", Value: "OverloadKind::DOUBLE")
213 .Case(S: "ResRetInt16Ty", Value: "OverloadKind::I16")
214 .Case(S: "ResRetInt32Ty", Value: "OverloadKind::I32")
215 .Case(S: "ResRetInt64Ty", Value: "OverloadKind::I64")
216 .Case(S: "CBufRetHalfTy", Value: "OverloadKind::HALF")
217 .Case(S: "CBufRetFloatTy", Value: "OverloadKind::FLOAT")
218 .Case(S: "CBufRetDoubleTy", Value: "OverloadKind::DOUBLE")
219 .Case(S: "CBufRetInt16Ty", Value: "OverloadKind::I16")
220 .Case(S: "CBufRetInt32Ty", Value: "OverloadKind::I32")
221 .Case(S: "CBufRetInt64Ty", Value: "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}, ...}
231static std::string getOverloadMaskString(ArrayRef<const Record *> Recs) {
232 std::string MaskString = "";
233 std::string Prefix = "";
234 MaskString.append(s: "{");
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(s: "{{1, 0}, OverloadKind::UNDEFINED}}");
240 } else {
241 for (const auto *Rec : Recs) {
242 unsigned Major =
243 Rec->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Major");
244 unsigned Minor =
245 Rec->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Minor");
246 MaskString.append(str: Prefix)
247 .append(s: "{{")
248 .append(str: std::to_string(val: Major))
249 .append(s: ", ")
250 .append(str: std::to_string(val: Minor).append(s: "}, "));
251
252 std::string PipePrefix = "";
253 auto Tys = Rec->getValueAsListOfDefs(FieldName: "overload_types");
254 if (Tys.empty()) {
255 MaskString.append(s: "OverloadKind::UNDEFINED");
256 }
257 for (const auto *Ty : Tys) {
258 MaskString.append(str: PipePrefix).append(svt: getOverloadKindStr(R: Ty));
259 PipePrefix = " | ";
260 }
261
262 MaskString.append(s: "}");
263 Prefix = ", ";
264 }
265 MaskString.append(s: "}");
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}, ...}
277static std::string getStageMaskString(ArrayRef<const Record *> Recs) {
278 std::string MaskString = "";
279 std::string Prefix = "";
280 MaskString.append(s: "{");
281 // Atleast one stage information record is expected to be specified.
282 if (Recs.empty()) {
283 PrintFatalError(Msg: "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(FieldName: "dxil_version")->getValueAsInt(FieldName: "Major");
289 unsigned Minor = Rec->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Minor");
290 MaskString.append(str: Prefix)
291 .append(s: "{{")
292 .append(str: std::to_string(val: Major))
293 .append(s: ", ")
294 .append(str: std::to_string(val: Minor).append(s: "}, "));
295
296 std::string PipePrefix = "";
297 auto Stages = Rec->getValueAsListOfDefs(FieldName: "shader_stages");
298 if (Stages.empty()) {
299 PrintFatalError(Msg: "No valid stages for operation specified");
300 }
301 for (const auto *S : Stages) {
302 MaskString.append(str: PipePrefix).append(s: "ShaderKind::").append(svt: S->getName());
303 PipePrefix = " | ";
304 }
305
306 MaskString.append(s: "}");
307 Prefix = ", ";
308 }
309 MaskString.append(s: "}");
310 return MaskString;
311}
312
313/// Emit a list valid DXIL Version records
314static void emitDXILVersions(const RecordKeeper &Records, raw_ostream &OS) {
315 OS << "#ifdef DXIL_VERSION\n";
316 for (const Record *Version : Records.getAllDerivedDefinitions(ClassName: "Version")) {
317 unsigned Major = Version->getValueAsInt(FieldName: "Major");
318 unsigned Minor = Version->getValueAsInt(FieldName: "Minor");
319 OS << "DXIL_VERSION(";
320 OS << std::to_string(val: Major) << ", " << std::to_string(val: 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
328static 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
338static void emitDXILOpClasses(const RecordKeeper &Records, raw_ostream &OS) {
339 OS << "#ifdef DXIL_OPCLASS\n";
340 for (const Record *OpClass : Records.getAllDerivedDefinitions(ClassName: "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
347static void emitDXILOpParamTypes(const RecordKeeper &Records, raw_ostream &OS) {
348 OS << "#ifdef DXIL_OP_PARAM_TYPE\n";
349 for (const Record *OpParamType :
350 Records.getAllDerivedDefinitions(ClassName: "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
357static void emitDXILAttributes(const RecordKeeper &Records, raw_ostream &OS) {
358 OS << "#ifdef DXIL_ATTRIBUTE\n";
359 for (const Record *Attr : Records.getAllDerivedDefinitions(ClassName: "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
367static 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
376static 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(FieldName: "dxil_version")->getValueAsInt(FieldName: "Major");
391 unsigned Minor =
392 Rec->getValueAsDef(FieldName: "dxil_version")->getValueAsInt(FieldName: "Minor");
393 OS << "DXIL_OP_ATTRIBUTES(dxil::OpCode::" << Op.OpName << ", ";
394 OS << std::to_string(val: Major) << ", " << std::to_string(val: Minor);
395 // These Attrs are the ones set for above DXIL version
396 auto Attrs = Rec->getValueAsListOfDefs(FieldName: "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(ClassName: "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
415static 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
435static 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(FieldName: "type")->getNameInitAsString();
450 int Value = ArgSelect->getValueAsInt(FieldName: "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
464static 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(ClassName: "IntrinArgSelectType")) {
469 StringRef StrippedName = StripIntrinArgSelectTypePrefix(Type: 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
479static 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(Seq: Op.OpName);
488
489 if (ClassSet.insert(key: Op.OpClass).second)
490 OpClassStrings.add(Seq: 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(Seq: Op.OpName) << ", OpCodeClass::" << Op.OpClass << ", "
507 << OpClassStrings.get(Seq: Op.OpClass.data()) << ", "
508 << getOverloadMaskString(Recs: Op.OverloadRecs) << ", "
509 << getStageMaskString(Recs: 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 Decl: " 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, Decl: " static const char DXILOpCodeClassNameTable[]");
545
546 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";
547 OS << " return DXILOpCodeClassNameTable + Index;\n";
548 OS << "}\n\n";
549}
550
551static void emitDXILOperationTableDataStructs(const RecordKeeper &Records,
552 raw_ostream &OS) {
553 // Get Shader stage records
554 std::vector<const Record *> ShaderKindRecs =
555 Records.getAllDerivedDefinitions(ClassName: "DXILShaderStage");
556 // Sort records by name
557 llvm::sort(C&: ShaderKindRecs, Comp: [](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(A: 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(RHS: Removed) == 0) {
574 OS << " " << Name
575 << " = 0, // Pseudo-stage indicating op not supported in any "
576 "stage\n";
577 } else if (Name.compare(RHS: AllStages) == 0) {
578 OS << " " << Name << " = 0x"
579 << utohexstr(X: ((1 << ShaderKindCount) - 1), LowerCase: false, Width: 0)
580 << ", // Pseudo-stage indicating op is supported in all stages\n";
581 } else if (Name.compare(RHS: AllStages)) {
582 OS << " " << Name << " = 1 << " << std::to_string(val: 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
591static 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(ClassName: "DXILOp")) {
597 DXILOps.emplace_back(args: DXILOperationDesc(R));
598 }
599 // Sort by opcode.
600 llvm::sort(C&: DXILOps,
601 Comp: [](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(Msg: Twine("Duplicate opcode: ") + Twine(Desc.OpCode));
608 PrevOp = Desc.OpCode;
609 }
610
611 emitDXILVersions(Records, OS);
612 emitDXILOpCodes(Ops: DXILOps, OS);
613 emitDXILOpClasses(Records, OS);
614 emitDXILOpParamTypes(Records, OS);
615 emitDXILAttributes(Records, OS);
616 emitDXILOpAttributes(Records, Ops: DXILOps, OS);
617 emitDXILOpFunctionTypes(Ops: DXILOps, OS);
618 emitDXILIntrinsicArgSelectTypes(Records, OS);
619 emitDXILIntrinsicMap(Ops: DXILOps, OS);
620 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n\n";
621 emitDXILOperationTableDataStructs(Records, OS);
622 emitDXILOperationTable(Ops: DXILOps, OS);
623 OS << "#undef DXIL_OP_OPERATION_TABLE\n";
624 OS << "#endif\n\n";
625}
626
627static TableGen::Emitter::Opt X("gen-dxil-operation", emitDxilOperation,
628 "Generate DXIL operation information");
629