1 //===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // DXILEmitter uses the descriptions of DXIL operation to construct enum and
10 // helper functions for DXIL operation.
12 //===----------------------------------------------------------------------===//
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"
31 using namespace llvm::dxil
;
35 struct DXILOperationDesc
{
36 std::string OpName
; // name of DXIL operation
37 int OpCode
; // ID of DXIL operation
38 StringRef OpClass
; // name of the opcode class
39 StringRef Doc
; // the documentation description of this instruction
40 // Vector of operand type records - return type is at index 0
41 SmallVector
<const Record
*> OpTypes
;
42 SmallVector
<const Record
*> OverloadRecs
;
43 SmallVector
<const Record
*> StageRecs
;
44 SmallVector
<const Record
*> AttrRecs
;
45 StringRef Intrinsic
; // The llvm intrinsic map to OpName. Default is "" which
46 // means no map exists
47 SmallVector
<StringRef
, 4>
48 ShaderStages
; // shader stages to which this applies, empty for all.
49 int OverloadParamIndex
; // Index of parameter with overload type.
50 // -1 : no overload types
51 SmallVector
<StringRef
, 4> Counters
; // counters for this inst.
52 DXILOperationDesc(const Record
*);
54 } // end anonymous namespace
56 /// In-place sort TableGen records of class with a field
57 /// Version dxil_version
58 /// in the ascending version order.
59 static void ascendingSortByVersion(std::vector
<const Record
*> &Recs
) {
60 sort(Recs
, [](const Record
*RecA
, const Record
*RecB
) {
62 RecA
->getValueAsDef("dxil_version")->getValueAsInt("Major");
64 RecA
->getValueAsDef("dxil_version")->getValueAsInt("Minor");
66 RecB
->getValueAsDef("dxil_version")->getValueAsInt("Major");
68 RecB
->getValueAsDef("dxil_version")->getValueAsInt("Minor");
70 return (VersionTuple(RecAMaj
, RecAMin
) < VersionTuple(RecBMaj
, RecBMin
));
74 /// Construct an object using the DXIL Operation records specified
75 /// in DXIL.td. This serves as the single source of reference of
76 /// the information extracted from the specified Record R, for
77 /// C++ code generated by this TableGen backend.
78 // \param R Object representing TableGen record of a DXIL Operation
79 DXILOperationDesc::DXILOperationDesc(const Record
*R
) {
80 OpName
= R
->getNameInitAsString();
81 OpCode
= R
->getValueAsInt("OpCode");
83 Doc
= R
->getValueAsString("Doc");
84 SmallVector
<const Record
*> ParamTypeRecs
;
86 ParamTypeRecs
.push_back(R
->getValueAsDef("result"));
88 for (const Record
*ArgTy
: R
->getValueAsListOfDefs("arguments")) {
89 ParamTypeRecs
.push_back(ArgTy
);
91 size_t ParamTypeRecsSize
= ParamTypeRecs
.size();
92 // Populate OpTypes with return type and parameter types
94 // Parameter indices of overloaded parameters.
95 // This vector contains overload parameters in the order used to
96 // resolve an LLVMMatchType in accordance with convention outlined in
97 // the comment before the definition of class LLVMMatchType in
98 // llvm/IR/Intrinsics.td
99 OverloadParamIndex
= -1; // A sigil meaning none.
100 for (unsigned I
= 0; I
< ParamTypeRecsSize
; I
++) {
101 const Record
*TR
= ParamTypeRecs
[I
];
102 // Track operation parameter indices of any overload types
103 if (TR
->getValueAsInt("isOverload")) {
104 if (OverloadParamIndex
!= -1) {
105 assert(TR
== ParamTypeRecs
[OverloadParamIndex
] &&
106 "Specification of multiple differing overload parameter types "
109 // Keep the earliest parameter index we see, but if it was the return type
110 // overwrite it with the first overloaded argument.
111 if (OverloadParamIndex
<= 0)
112 OverloadParamIndex
= I
;
114 OpTypes
.emplace_back(TR
);
117 // Get overload records
118 std::vector
<const Record
*> Recs
= R
->getValueAsListOfDefs("overloads");
120 // Sort records in ascending order of DXIL version
121 ascendingSortByVersion(Recs
);
123 for (const Record
*CR
: Recs
) {
124 OverloadRecs
.push_back(CR
);
128 Recs
= R
->getValueAsListOfDefs("stages");
131 PrintFatalError(R
, Twine("Atleast one specification of valid stage for ") +
132 OpName
+ " is required");
135 // Sort records in ascending order of DXIL version
136 ascendingSortByVersion(Recs
);
138 for (const Record
*CR
: Recs
) {
139 StageRecs
.push_back(CR
);
142 // Get attribute records
143 Recs
= R
->getValueAsListOfDefs("attributes");
145 // Sort records in ascending order of DXIL version
146 ascendingSortByVersion(Recs
);
148 for (const Record
*CR
: Recs
) {
149 AttrRecs
.push_back(CR
);
152 // Get the operation class
153 OpClass
= R
->getValueAsDef("OpClass")->getName();
155 if (!OpClass
.str().compare("UnknownOpClass")) {
156 PrintFatalError(R
, Twine("Unspecified DXIL OpClass for DXIL operation - ") +
160 const RecordVal
*RV
= R
->getValue("LLVMIntrinsic");
161 if (RV
&& RV
->getValue()) {
162 if (const DefInit
*DI
= dyn_cast
<DefInit
>(RV
->getValue())) {
163 auto *IntrinsicDef
= DI
->getDef();
164 auto DefName
= IntrinsicDef
->getName();
165 assert(DefName
.starts_with("int_") && "invalid intrinsic name");
166 // Remove the int_ from intrinsic name.
167 Intrinsic
= DefName
.substr(4);
172 /// Return a string representation of OverloadKind enum that maps to
173 /// input LLVMType record
174 /// \param R TableGen def record of class LLVMType
175 /// \return std::string string representation of OverloadKind
177 static StringRef
getOverloadKindStr(const Record
*R
) {
178 // TODO: This is a hack. We need to rework how we're handling the set of
179 // overloads to avoid this business with the separate OverloadKind enum.
180 return StringSwitch
<StringRef
>(R
->getName())
181 .Case("HalfTy", "OverloadKind::HALF")
182 .Case("FloatTy", "OverloadKind::FLOAT")
183 .Case("DoubleTy", "OverloadKind::DOUBLE")
184 .Case("Int1Ty", "OverloadKind::I1")
185 .Case("Int8Ty", "OverloadKind::I8")
186 .Case("Int16Ty", "OverloadKind::I16")
187 .Case("Int32Ty", "OverloadKind::I32")
188 .Case("Int64Ty", "OverloadKind::I64")
189 .Case("ResRetHalfTy", "OverloadKind::HALF")
190 .Case("ResRetFloatTy", "OverloadKind::FLOAT")
191 .Case("ResRetInt16Ty", "OverloadKind::I16")
192 .Case("ResRetInt32Ty", "OverloadKind::I32");
195 /// Return a string representation of valid overload information denoted
198 /// \param Recs A vector of records of TableGen Overload records
199 /// \return std::string string representation of overload mask string
200 /// predicated by DXIL Version. E.g.,
201 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...}
202 static std::string
getOverloadMaskString(ArrayRef
<const Record
*> Recs
) {
203 std::string MaskString
= "";
204 std::string Prefix
= "";
205 MaskString
.append("{");
206 // If no overload information records were specified, assume the operation
207 // a) to be supported in DXIL Version 1.0 and later
208 // b) has no overload types
210 MaskString
.append("{{1, 0}, OverloadKind::UNDEFINED}}");
212 for (const auto *Rec
: Recs
) {
214 Rec
->getValueAsDef("dxil_version")->getValueAsInt("Major");
216 Rec
->getValueAsDef("dxil_version")->getValueAsInt("Minor");
217 MaskString
.append(Prefix
)
219 .append(std::to_string(Major
))
221 .append(std::to_string(Minor
).append("}, "));
223 std::string PipePrefix
= "";
224 auto Tys
= Rec
->getValueAsListOfDefs("overload_types");
226 MaskString
.append("OverloadKind::UNDEFINED");
228 for (const auto *Ty
: Tys
) {
229 MaskString
.append(PipePrefix
).append(getOverloadKindStr(Ty
));
233 MaskString
.append("}");
236 MaskString
.append("}");
241 /// Return a string representation of valid shader stag information denoted
244 /// \param Recs A vector of records of TableGen Stages records
245 /// \return std::string string representation of stages mask string
246 /// predicated by DXIL Version. E.g.,
247 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...}
248 static std::string
getStageMaskString(ArrayRef
<const Record
*> Recs
) {
249 std::string MaskString
= "";
250 std::string Prefix
= "";
251 MaskString
.append("{");
252 // Atleast one stage information record is expected to be specified.
254 PrintFatalError("Atleast one specification of valid stages for "
255 "operation must be specified");
258 for (const auto *Rec
: Recs
) {
259 unsigned Major
= Rec
->getValueAsDef("dxil_version")->getValueAsInt("Major");
260 unsigned Minor
= Rec
->getValueAsDef("dxil_version")->getValueAsInt("Minor");
261 MaskString
.append(Prefix
)
263 .append(std::to_string(Major
))
265 .append(std::to_string(Minor
).append("}, "));
267 std::string PipePrefix
= "";
268 auto Stages
= Rec
->getValueAsListOfDefs("shader_stages");
269 if (Stages
.empty()) {
270 PrintFatalError("No valid stages for operation specified");
272 for (const auto *S
: Stages
) {
273 MaskString
.append(PipePrefix
).append("ShaderKind::").append(S
->getName());
277 MaskString
.append("}");
280 MaskString
.append("}");
284 /// Return a string representation of valid attribute information denoted
287 /// \param Recs A vector of records of TableGen Attribute records
288 /// \return std::string string representation of stages mask string
289 /// predicated by DXIL Version. E.g.,
290 // {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...}
291 static std::string
getAttributeMaskString(ArrayRef
<const Record
*> Recs
) {
292 std::string MaskString
= "";
293 std::string Prefix
= "";
294 MaskString
.append("{");
296 for (const auto *Rec
: Recs
) {
297 unsigned Major
= Rec
->getValueAsDef("dxil_version")->getValueAsInt("Major");
298 unsigned Minor
= Rec
->getValueAsDef("dxil_version")->getValueAsInt("Minor");
299 MaskString
.append(Prefix
)
301 .append(std::to_string(Major
))
303 .append(std::to_string(Minor
).append("}, "));
305 std::string PipePrefix
= "";
306 auto Attrs
= Rec
->getValueAsListOfDefs("op_attrs");
308 MaskString
.append("Attribute::None");
310 for (const auto *Attr
: Attrs
) {
311 MaskString
.append(PipePrefix
)
312 .append("Attribute::")
313 .append(Attr
->getName());
318 MaskString
.append("}");
321 MaskString
.append("}");
325 /// Emit a mapping of DXIL opcode to opname
326 static void emitDXILOpCodes(ArrayRef
<DXILOperationDesc
> Ops
, raw_ostream
&OS
) {
327 OS
<< "#ifdef DXIL_OPCODE\n";
328 for (const DXILOperationDesc
&Op
: Ops
)
329 OS
<< "DXIL_OPCODE(" << Op
.OpCode
<< ", " << Op
.OpName
<< ")\n";
330 OS
<< "#undef DXIL_OPCODE\n";
335 /// Emit a list of DXIL op classes
336 static void emitDXILOpClasses(const RecordKeeper
&Records
, raw_ostream
&OS
) {
337 OS
<< "#ifdef DXIL_OPCLASS\n";
338 for (const Record
*OpClass
: Records
.getAllDerivedDefinitions("DXILOpClass"))
339 OS
<< "DXIL_OPCLASS(" << OpClass
->getName() << ")\n";
340 OS
<< "#undef DXIL_OPCLASS\n";
344 /// Emit a list of DXIL op parameter types
345 static void emitDXILOpParamTypes(const RecordKeeper
&Records
, raw_ostream
&OS
) {
346 OS
<< "#ifdef DXIL_OP_PARAM_TYPE\n";
347 for (const Record
*OpParamType
:
348 Records
.getAllDerivedDefinitions("DXILOpParamType"))
349 OS
<< "DXIL_OP_PARAM_TYPE(" << OpParamType
->getName() << ")\n";
350 OS
<< "#undef DXIL_OP_PARAM_TYPE\n";
354 /// Emit a list of DXIL op function types
355 static void emitDXILOpFunctionTypes(ArrayRef
<DXILOperationDesc
> Ops
,
357 OS
<< "#ifndef DXIL_OP_FUNCTION_TYPE\n";
358 OS
<< "#define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...)\n";
360 for (const DXILOperationDesc
&Op
: Ops
) {
361 OS
<< "DXIL_OP_FUNCTION_TYPE(dxil::OpCode::" << Op
.OpName
;
362 for (const Record
*Rec
: Op
.OpTypes
)
363 OS
<< ", dxil::OpParamType::" << Rec
->getName();
364 // If there are no arguments, we need an empty comma for the varargs
365 if (Op
.OpTypes
.size() == 1)
369 OS
<< "#undef DXIL_OP_FUNCTION_TYPE\n";
372 /// Emit map of DXIL operation to LLVM or DirectX intrinsic
373 /// \param A vector of DXIL Ops
374 /// \param Output stream
375 static void emitDXILIntrinsicMap(ArrayRef
<DXILOperationDesc
> Ops
,
377 OS
<< "#ifdef DXIL_OP_INTRINSIC\n";
379 for (const auto &Op
: Ops
) {
380 if (Op
.Intrinsic
.empty())
382 OS
<< "DXIL_OP_INTRINSIC(dxil::OpCode::" << Op
.OpName
383 << ", Intrinsic::" << Op
.Intrinsic
<< ")\n";
386 OS
<< "#undef DXIL_OP_INTRINSIC\n";
390 /// Emit DXIL operation table
391 /// \param A vector of DXIL Ops
392 /// \param Output stream
393 static void emitDXILOperationTable(ArrayRef
<DXILOperationDesc
> Ops
,
396 SequenceToOffsetTable
<std::string
> OpClassStrings
;
397 SequenceToOffsetTable
<std::string
> OpStrings
;
399 StringSet
<> ClassSet
;
400 for (const auto &Op
: Ops
) {
401 OpStrings
.add(Op
.OpName
);
403 if (ClassSet
.insert(Op
.OpClass
).second
)
404 OpClassStrings
.add(Op
.OpClass
.data());
409 OpClassStrings
.layout();
411 // Emit access function getOpcodeProperty() that embeds DXIL Operation table
412 // with entries of type struct OpcodeProperty.
413 OS
<< "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) "
416 OS
<< " static const OpCodeProperty OpCodeProps[] = {\n";
417 std::string Prefix
= "";
418 for (const auto &Op
: Ops
) {
419 OS
<< Prefix
<< " { dxil::OpCode::" << Op
.OpName
<< ", "
420 << OpStrings
.get(Op
.OpName
) << ", OpCodeClass::" << Op
.OpClass
<< ", "
421 << OpClassStrings
.get(Op
.OpClass
.data()) << ", "
422 << getOverloadMaskString(Op
.OverloadRecs
) << ", "
423 << getStageMaskString(Op
.StageRecs
) << ", "
424 << getAttributeMaskString(Op
.AttrRecs
) << ", " << Op
.OverloadParamIndex
430 OS
<< " // FIXME: change search to indexing with\n";
431 OS
<< " // Op once all DXIL operations are added.\n";
432 OS
<< " OpCodeProperty TmpProp;\n";
433 OS
<< " TmpProp.OpCode = Op;\n";
434 OS
<< " const OpCodeProperty *Prop =\n";
435 OS
<< " llvm::lower_bound(OpCodeProps, TmpProp,\n";
436 OS
<< " [](const OpCodeProperty &A, const "
437 "OpCodeProperty &B) {\n";
438 OS
<< " return A.OpCode < B.OpCode;\n";
440 OS
<< " assert(Prop && \"failed to find OpCodeProperty\");\n";
441 OS
<< " return Prop;\n";
444 // Emit the string tables.
445 OS
<< "static const char *getOpCodeName(dxil::OpCode Op) {\n\n";
447 OpStrings
.emitStringLiteralDef(OS
,
448 " static const char DXILOpCodeNameTable[]");
450 OS
<< " auto *Prop = getOpCodeProperty(Op);\n";
451 OS
<< " unsigned Index = Prop->OpCodeNameOffset;\n";
452 OS
<< " return DXILOpCodeNameTable + Index;\n";
455 OS
<< "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
458 OpClassStrings
.emitStringLiteralDef(
459 OS
, " static const char DXILOpCodeClassNameTable[]");
461 OS
<< " unsigned Index = Prop.OpCodeClassNameOffset;\n";
462 OS
<< " return DXILOpCodeClassNameTable + Index;\n";
466 static void emitDXILOperationTableDataStructs(const RecordKeeper
&Records
,
468 // Get Shader stage records
469 std::vector
<const Record
*> ShaderKindRecs
=
470 Records
.getAllDerivedDefinitions("DXILShaderStage");
471 // Sort records by name
472 llvm::sort(ShaderKindRecs
, [](const Record
*A
, const Record
*B
) {
473 return A
->getName() < B
->getName();
476 OS
<< "// Valid shader kinds\n\n";
477 // Choose the type of enum ShaderKind based on the number of stages declared.
478 // This gives the flexibility to just add add new stage records in DXIL.td, if
479 // needed, with no need to change this backend code.
480 size_t ShaderKindCount
= ShaderKindRecs
.size();
481 uint64_t ShaderKindTySz
= PowerOf2Ceil(ShaderKindRecs
.size() + 1);
482 OS
<< "enum ShaderKind : uint" << ShaderKindTySz
<< "_t {\n";
483 const std::string
AllStages("all_stages");
484 const std::string
Removed("removed");
486 for (const auto *R
: ShaderKindRecs
) {
487 auto Name
= R
->getName();
488 if (Name
.compare(Removed
) == 0) {
490 << " = 0, // Pseudo-stage indicating op not supported in any "
492 } else if (Name
.compare(AllStages
) == 0) {
493 OS
<< " " << Name
<< " = 0x"
494 << utohexstr(((1 << ShaderKindCount
) - 1), false, 0)
495 << ", // Pseudo-stage indicating op is supported in all stages\n";
496 } else if (Name
.compare(AllStages
)) {
497 OS
<< " " << Name
<< " = 1 << " << std::to_string(ShiftVal
++) << ",\n";
500 OS
<< "}; // enum ShaderKind\n\n";
503 /// Entry function call that invokes the functionality of this TableGen backend
504 /// \param Records TableGen records of DXIL Operations defined in DXIL.td
505 /// \param OS output stream
506 static void emitDxilOperation(const RecordKeeper
&Records
, raw_ostream
&OS
) {
507 OS
<< "// Generated code, do not edit.\n";
509 // Get all DXIL Ops property records
510 std::vector
<DXILOperationDesc
> DXILOps
;
511 for (const Record
*R
: Records
.getAllDerivedDefinitions("DXILOp")) {
512 DXILOps
.emplace_back(DXILOperationDesc(R
));
516 [](const DXILOperationDesc
&A
, const DXILOperationDesc
&B
) {
517 return A
.OpCode
< B
.OpCode
;
520 for (const DXILOperationDesc
&Desc
: DXILOps
) {
521 if (Desc
.OpCode
== PrevOp
)
522 PrintFatalError(Twine("Duplicate opcode: ") + Twine(Desc
.OpCode
));
523 PrevOp
= Desc
.OpCode
;
526 emitDXILOpCodes(DXILOps
, OS
);
527 emitDXILOpClasses(Records
, OS
);
528 emitDXILOpParamTypes(Records
, OS
);
529 emitDXILOpFunctionTypes(DXILOps
, OS
);
530 emitDXILIntrinsicMap(DXILOps
, OS
);
531 OS
<< "#ifdef DXIL_OP_OPERATION_TABLE\n\n";
532 emitDXILOperationTableDataStructs(Records
, OS
);
533 emitDXILOperationTable(DXILOps
, OS
);
534 OS
<< "#undef DXIL_OP_OPERATION_TABLE\n";
538 static TableGen::Emitter::Opt
X("gen-dxil-operation", emitDxilOperation
,
539 "Generate DXIL operation information");