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 "SequenceToOffsetTable.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/ADT/StringSwitch.h"
19 #include "llvm/Support/DXILOperationCommon.h"
20 #include "llvm/TableGen/Record.h"
21 #include "llvm/TableGen/TableGenBackend.h"
24 using namespace llvm::dxil
;
28 struct DXILShaderModel
{
34 int Pos
; // position in parameter list
36 StringRef Name
; // short, unique name
37 StringRef Doc
; // the documentation description of this parameter
38 bool IsConst
; // whether this argument requires a constant value in the IR
39 StringRef EnumName
; // the name of the enum type if applicable
40 int MaxValue
; // the maximum value for this parameter if applicable
41 DXILParam(const Record
*R
);
44 struct DXILOperationData
{
45 StringRef Name
; // short, unique name
47 StringRef DXILOp
; // name of DXIL operation
48 int DXILOpID
; // ID of DXIL operation
49 StringRef DXILClass
; // name of the opcode class
50 StringRef Category
; // classification for this instruction
51 StringRef Doc
; // the documentation description of this instruction
53 SmallVector
<DXILParam
> Params
; // the operands that this instruction takes
54 StringRef OverloadTypes
; // overload types if applicable
55 StringRef FnAttr
; // attribute shorthands: rn=does not access
56 // memory,ro=only reads from memory
57 StringRef Intrinsic
; // The llvm intrinsic map to DXILOp. Default is "" which
59 bool IsDeriv
= false; // whether this is some kind of derivative
60 bool IsGradient
= false; // whether this requires a gradient calculation
61 bool IsFeedback
= false; // whether this is a sampler feedback op
62 bool IsWave
= false; // whether this requires in-wave, cross-lane functionality
63 bool RequiresUniformInputs
= false; // whether this operation requires that
64 // all of its inputs are uniform across
66 SmallVector
<StringRef
, 4>
67 ShaderStages
; // shader stages to which this applies, empty for all.
68 DXILShaderModel ShaderModel
; // minimum shader model required
69 DXILShaderModel ShaderModelTranslated
; // minimum shader model required with
70 // translation by linker
71 int OverloadParamIndex
; // parameter index which control the overload.
72 // When < 0, should be only 1 overload type.
73 SmallVector
<StringRef
, 4> counters
; // counters for this inst.
74 DXILOperationData(const Record
*R
) {
75 Name
= R
->getValueAsString("name");
76 DXILOp
= R
->getValueAsString("dxil_op");
77 DXILOpID
= R
->getValueAsInt("dxil_opid");
78 DXILClass
= R
->getValueAsDef("op_class")->getValueAsString("name");
79 Category
= R
->getValueAsDef("category")->getValueAsString("name");
81 if (R
->getValue("llvm_intrinsic")) {
82 auto *IntrinsicDef
= R
->getValueAsDef("llvm_intrinsic");
83 auto DefName
= IntrinsicDef
->getName();
84 assert(DefName
.startswith("int_") && "invalid intrinsic name");
85 // Remove the int_ from intrinsic name.
86 Intrinsic
= DefName
.substr(4);
89 Doc
= R
->getValueAsString("doc");
91 ListInit
*ParamList
= R
->getValueAsListInit("ops");
92 OverloadParamIndex
= -1;
93 for (unsigned I
= 0; I
< ParamList
->size(); ++I
) {
94 Record
*Param
= ParamList
->getElementAsRecord(I
);
95 Params
.emplace_back(DXILParam(Param
));
96 auto &CurParam
= Params
.back();
97 if (CurParam
.Kind
>= ParameterKind::OVERLOAD
)
98 OverloadParamIndex
= I
;
100 OverloadTypes
= R
->getValueAsString("oload_types");
101 FnAttr
= R
->getValueAsString("fn_attr");
104 } // end anonymous namespace
106 DXILParam::DXILParam(const Record
*R
) {
107 Name
= R
->getValueAsString("name");
108 Pos
= R
->getValueAsInt("pos");
109 Kind
= parameterTypeNameToKind(R
->getValueAsString("llvm_type"));
110 if (R
->getValue("doc"))
111 Doc
= R
->getValueAsString("doc");
112 IsConst
= R
->getValueAsBit("is_const");
113 EnumName
= R
->getValueAsString("enum_name");
114 MaxValue
= R
->getValueAsInt("max_value");
117 static std::string
parameterKindToString(ParameterKind Kind
) {
119 case ParameterKind::INVALID
:
121 case ParameterKind::VOID
:
123 case ParameterKind::HALF
:
125 case ParameterKind::FLOAT
:
127 case ParameterKind::DOUBLE
:
129 case ParameterKind::I1
:
131 case ParameterKind::I8
:
133 case ParameterKind::I16
:
135 case ParameterKind::I32
:
137 case ParameterKind::I64
:
139 case ParameterKind::OVERLOAD
:
141 case ParameterKind::CBUFFER_RET
:
142 return "CBUFFER_RET";
143 case ParameterKind::RESOURCE_RET
:
144 return "RESOURCE_RET";
145 case ParameterKind::DXIL_HANDLE
:
146 return "DXIL_HANDLE";
148 llvm_unreachable("Unknown llvm::dxil::ParameterKind enum");
151 static void emitDXILOpEnum(DXILOperationData
&DXILOp
, raw_ostream
&OS
) {
153 OS
<< DXILOp
.Name
<< " = " << DXILOp
.DXILOpID
<< ", // " << DXILOp
.Doc
157 static std::string
buildCategoryStr(StringSet
<> &Cetegorys
) {
159 raw_string_ostream
OS(Str
);
160 for (auto &It
: Cetegorys
) {
161 OS
<< " " << It
.getKey();
166 // Emit enum declaration for DXIL.
167 static void emitDXILEnums(std::vector
<DXILOperationData
> &DXILOps
,
169 // Sort by Category + OpName.
170 llvm::sort(DXILOps
, [](DXILOperationData
&A
, DXILOperationData
&B
) {
171 // Group by Category first.
172 if (A
.Category
== B
.Category
)
173 // Inside same Category, order by OpName.
174 return A
.DXILOp
< B
.DXILOp
;
176 return A
.Category
< B
.Category
;
179 OS
<< "// Enumeration for operations specified by DXIL\n";
180 OS
<< "enum class OpCode : unsigned {\n";
182 StringMap
<StringSet
<>> ClassMap
;
183 StringRef PrevCategory
= "";
184 for (auto &DXILOp
: DXILOps
) {
185 StringRef Category
= DXILOp
.Category
;
186 if (Category
!= PrevCategory
) {
187 OS
<< "\n// " << Category
<< "\n";
188 PrevCategory
= Category
;
190 emitDXILOpEnum(DXILOp
, OS
);
191 auto It
= ClassMap
.find(DXILOp
.DXILClass
);
192 if (It
!= ClassMap
.end()) {
193 It
->second
.insert(DXILOp
.Category
);
195 ClassMap
[DXILOp
.DXILClass
].insert(DXILOp
.Category
);
201 std::vector
<std::pair
<std::string
, std::string
>> ClassVec
;
202 for (auto &It
: ClassMap
) {
203 ClassVec
.emplace_back(
204 std::make_pair(It
.getKey().str(), buildCategoryStr(It
.second
)));
206 // Sort by Category + ClassName.
207 llvm::sort(ClassVec
, [](std::pair
<std::string
, std::string
> &A
,
208 std::pair
<std::string
, std::string
> &B
) {
209 StringRef ClassA
= A
.first
;
210 StringRef CategoryA
= A
.second
;
211 StringRef ClassB
= B
.first
;
212 StringRef CategoryB
= B
.second
;
213 // Group by Category first.
214 if (CategoryA
== CategoryB
)
215 // Inside same Category, order by ClassName.
216 return ClassA
< ClassB
;
218 return CategoryA
< CategoryB
;
221 OS
<< "// Groups for DXIL operations with equivalent function templates\n";
222 OS
<< "enum class OpCodeClass : unsigned {\n";
224 for (auto &It
: ClassVec
) {
226 StringRef Category
= It
.second
;
227 if (Category
!= PrevCategory
) {
228 OS
<< "\n// " << Category
<< "\n";
229 PrevCategory
= Category
;
231 StringRef Name
= It
.first
;
237 // Emit map from llvm intrinsic to DXIL operation.
238 static void emitDXILIntrinsicMap(std::vector
<DXILOperationData
> &DXILOps
,
241 // FIXME: use array instead of SmallDenseMap.
242 OS
<< "static const SmallDenseMap<Intrinsic::ID, dxil::OpCode> LowerMap = "
244 for (auto &DXILOp
: DXILOps
) {
245 if (DXILOp
.Intrinsic
.empty())
247 // {Intrinsic::sin, dxil::OpCode::Sin},
248 OS
<< " { Intrinsic::" << DXILOp
.Intrinsic
249 << ", dxil::OpCode::" << DXILOp
.DXILOp
<< "},\n";
255 static std::string
emitDXILOperationFnAttr(StringRef FnAttr
) {
256 return StringSwitch
<std::string
>(FnAttr
)
257 .Case("rn", "Attribute::ReadNone")
258 .Case("ro", "Attribute::ReadOnly")
259 .Default("Attribute::None");
262 static std::string
getOverloadKind(StringRef Overload
) {
263 return StringSwitch
<std::string
>(Overload
)
264 .Case("half", "OverloadKind::HALF")
265 .Case("float", "OverloadKind::FLOAT")
266 .Case("double", "OverloadKind::DOUBLE")
267 .Case("i1", "OverloadKind::I1")
268 .Case("i16", "OverloadKind::I16")
269 .Case("i32", "OverloadKind::I32")
270 .Case("i64", "OverloadKind::I64")
271 .Case("udt", "OverloadKind::UserDefineType")
272 .Case("obj", "OverloadKind::ObjectType")
273 .Default("OverloadKind::VOID");
276 static std::string
getDXILOperationOverload(StringRef Overloads
) {
277 SmallVector
<StringRef
> OverloadStrs
;
278 Overloads
.split(OverloadStrs
, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false);
279 // Format is: OverloadKind::FLOAT | OverloadKind::HALF
280 assert(!OverloadStrs
.empty() && "Invalid overloads");
281 auto It
= OverloadStrs
.begin();
283 raw_string_ostream
OS(Result
);
284 OS
<< getOverloadKind(*It
);
285 for (++It
; It
!= OverloadStrs
.end(); ++It
) {
286 OS
<< " | " << getOverloadKind(*It
);
291 static std::string
lowerFirstLetter(StringRef Name
) {
295 std::string LowerName
= Name
.str();
296 LowerName
[0] = llvm::toLower(Name
[0]);
300 static std::string
getDXILOpClassName(StringRef DXILOpClass
) {
301 // Lower first letter expect for special case.
302 return StringSwitch
<std::string
>(DXILOpClass
)
303 .Case("CBufferLoad", "cbufferLoad")
304 .Case("CBufferLoadLegacy", "cbufferLoadLegacy")
305 .Case("GSInstanceID", "gsInstanceID")
306 .Default(lowerFirstLetter(DXILOpClass
));
309 static void emitDXILOperationTable(std::vector
<DXILOperationData
> &DXILOps
,
312 llvm::sort(DXILOps
, [](DXILOperationData
&A
, DXILOperationData
&B
) {
313 return A
.DXILOpID
< B
.DXILOpID
;
317 SequenceToOffsetTable
<std::string
> OpClassStrings
;
318 SequenceToOffsetTable
<std::string
> OpStrings
;
319 SequenceToOffsetTable
<SmallVector
<ParameterKind
>> Parameters
;
321 StringMap
<SmallVector
<ParameterKind
>> ParameterMap
;
322 StringSet
<> ClassSet
;
323 for (auto &DXILOp
: DXILOps
) {
324 OpStrings
.add(DXILOp
.DXILOp
.str());
326 if (ClassSet
.contains(DXILOp
.DXILClass
))
328 ClassSet
.insert(DXILOp
.DXILClass
);
329 OpClassStrings
.add(getDXILOpClassName(DXILOp
.DXILClass
));
330 SmallVector
<ParameterKind
> ParamKindVec
;
331 for (auto &Param
: DXILOp
.Params
) {
332 ParamKindVec
.emplace_back(Param
.Kind
);
334 ParameterMap
[DXILOp
.DXILClass
] = ParamKindVec
;
335 Parameters
.add(ParamKindVec
);
340 OpClassStrings
.layout();
343 // Emit the DXIL operation table.
344 //{dxil::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary,
345 // OpCodeClassNameIndex,
346 // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0,
347 // 3, ParameterTableOffset},
348 OS
<< "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode DXILOp) "
351 OS
<< " static const OpCodeProperty OpCodeProps[] = {\n";
352 for (auto &DXILOp
: DXILOps
) {
353 OS
<< " { dxil::OpCode::" << DXILOp
.DXILOp
<< ", "
354 << OpStrings
.get(DXILOp
.DXILOp
.str())
355 << ", OpCodeClass::" << DXILOp
.DXILClass
<< ", "
356 << OpClassStrings
.get(getDXILOpClassName(DXILOp
.DXILClass
)) << ", "
357 << getDXILOperationOverload(DXILOp
.OverloadTypes
) << ", "
358 << emitDXILOperationFnAttr(DXILOp
.FnAttr
) << ", "
359 << DXILOp
.OverloadParamIndex
<< ", " << DXILOp
.Params
.size() << ", "
360 << Parameters
.get(ParameterMap
[DXILOp
.DXILClass
]) << " },\n";
364 OS
<< " // FIXME: change search to indexing with\n";
365 OS
<< " // DXILOp once all DXIL op is added.\n";
366 OS
<< " OpCodeProperty TmpProp;\n";
367 OS
<< " TmpProp.OpCode = DXILOp;\n";
368 OS
<< " const OpCodeProperty *Prop =\n";
369 OS
<< " llvm::lower_bound(OpCodeProps, TmpProp,\n";
370 OS
<< " [](const OpCodeProperty &A, const "
371 "OpCodeProperty &B) {\n";
372 OS
<< " return A.OpCode < B.OpCode;\n";
374 OS
<< " assert(Prop && \"fail to find OpCodeProperty\");\n";
375 OS
<< " return Prop;\n";
378 // Emit the string tables.
379 OS
<< "static const char *getOpCodeName(dxil::OpCode DXILOp) {\n\n";
381 OpStrings
.emitStringLiteralDef(OS
,
382 " static const char DXILOpCodeNameTable[]");
384 OS
<< " auto *Prop = getOpCodeProperty(DXILOp);\n";
385 OS
<< " unsigned Index = Prop->OpCodeNameOffset;\n";
386 OS
<< " return DXILOpCodeNameTable + Index;\n";
389 OS
<< "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
392 OpClassStrings
.emitStringLiteralDef(
393 OS
, " static const char DXILOpCodeClassNameTable[]");
395 OS
<< " unsigned Index = Prop.OpCodeClassNameOffset;\n";
396 OS
<< " return DXILOpCodeClassNameTable + Index;\n";
399 OS
<< "static const ParameterKind *getOpCodeParameterKind(const "
400 "OpCodeProperty &Prop) "
402 OS
<< " static const ParameterKind DXILOpParameterKindTable[] = {\n";
405 [](raw_ostream
&ParamOS
, ParameterKind Kind
) {
406 ParamOS
<< "ParameterKind::" << parameterKindToString(Kind
);
408 "ParameterKind::INVALID");
410 OS
<< " unsigned Index = Prop.ParameterTableOffset;\n";
411 OS
<< " return DXILOpParameterKindTable + Index;\n";
415 static void EmitDXILOperation(RecordKeeper
&Records
, raw_ostream
&OS
) {
416 std::vector
<Record
*> Ops
= Records
.getAllDerivedDefinitions("dxil_op");
417 OS
<< "// Generated code, do not edit.\n";
420 std::vector
<DXILOperationData
> DXILOps
;
421 DXILOps
.reserve(Ops
.size());
422 for (auto *Record
: Ops
) {
423 DXILOps
.emplace_back(DXILOperationData(Record
));
426 OS
<< "#ifdef DXIL_OP_ENUM\n";
427 emitDXILEnums(DXILOps
, OS
);
430 OS
<< "#ifdef DXIL_OP_INTRINSIC_MAP\n";
431 emitDXILIntrinsicMap(DXILOps
, OS
);
434 OS
<< "#ifdef DXIL_OP_OPERATION_TABLE\n";
435 emitDXILOperationTable(DXILOps
, OS
);
441 static TableGen::Emitter::Opt
X("gen-dxil-operation", EmitDXILOperation
,
442 "Generate DXIL operation information");