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/TableGen/Error.h"
20 #include "llvm/TableGen/Record.h"
26 struct DXILShaderModel
{
31 int Pos
; // position in parameter list
32 StringRef Type
; // llvm type name, $o for overload, $r for resource
33 // type, $cb for legacy cbuffer, $u4 for u4 struct
34 StringRef Name
; // short, unique name
35 StringRef Doc
; // the documentation description of this parameter
36 bool IsConst
; // whether this argument requires a constant value in the IR
37 StringRef EnumName
; // the name of the enum type if applicable
38 int MaxValue
; // the maximum value for this parameter if applicable
39 DXILParam(const Record
*R
) {
40 Name
= R
->getValueAsString("name");
41 Pos
= R
->getValueAsInt("pos");
42 Type
= R
->getValueAsString("llvm_type");
43 if (R
->getValue("doc"))
44 Doc
= R
->getValueAsString("doc");
45 IsConst
= R
->getValueAsBit("is_const");
46 EnumName
= R
->getValueAsString("enum_name");
47 MaxValue
= R
->getValueAsInt("max_value");
51 struct DXILOperationData
{
52 StringRef Name
; // short, unique name
54 StringRef DXILOp
; // name of DXIL operation
55 int DXILOpID
; // ID of DXIL operation
56 StringRef DXILClass
; // name of the opcode class
57 StringRef Category
; // classification for this instruction
58 StringRef Doc
; // the documentation description of this instruction
60 SmallVector
<DXILParam
> Params
; // the operands that this instruction takes
61 StringRef OverloadTypes
; // overload types if applicable
62 StringRef FnAttr
; // attribute shorthands: rn=does not access
63 // memory,ro=only reads from memory
64 StringRef Intrinsic
; // The llvm intrinsic map to DXILOp. Default is "" which
66 bool IsDeriv
; // whether this is some kind of derivative
67 bool IsGradient
; // whether this requires a gradient calculation
68 bool IsFeedback
; // whether this is a sampler feedback op
69 bool IsWave
; // whether this requires in-wave, cross-lane functionality
70 bool RequiresUniformInputs
; // whether this operation requires that all
71 // of its inputs are uniform across the wave
72 SmallVector
<StringRef
, 4>
73 ShaderStages
; // shader stages to which this applies, empty for all.
74 DXILShaderModel ShaderModel
; // minimum shader model required
75 DXILShaderModel ShaderModelTranslated
; // minimum shader model required with
76 // translation by linker
77 SmallVector
<StringRef
, 4> counters
; // counters for this inst.
78 DXILOperationData(const Record
*R
) {
79 Name
= R
->getValueAsString("name");
80 DXILOp
= R
->getValueAsString("dxil_op");
81 DXILOpID
= R
->getValueAsInt("dxil_opid");
82 DXILClass
= R
->getValueAsDef("op_class")->getValueAsString("name");
83 Category
= R
->getValueAsDef("category")->getValueAsString("name");
85 if (R
->getValue("llvm_intrinsic")) {
86 auto *IntrinsicDef
= R
->getValueAsDef("llvm_intrinsic");
87 auto DefName
= IntrinsicDef
->getName();
88 assert(DefName
.startswith("int_") && "invalid intrinsic name");
89 // Remove the int_ from intrinsic name.
90 Intrinsic
= DefName
.substr(4);
93 Doc
= R
->getValueAsString("doc");
95 ListInit
*ParamList
= R
->getValueAsListInit("ops");
96 for (unsigned i
= 0; i
< ParamList
->size(); ++i
) {
97 Record
*Param
= ParamList
->getElementAsRecord(i
);
98 Params
.emplace_back(DXILParam(Param
));
100 OverloadTypes
= R
->getValueAsString("oload_types");
101 FnAttr
= R
->getValueAsString("fn_attr");
104 } // end anonymous namespace
106 static void emitDXILOpEnum(DXILOperationData
&DXILOp
, raw_ostream
&OS
) {
108 OS
<< DXILOp
.Name
<< " = " << DXILOp
.DXILOpID
<< ", // " << DXILOp
.Doc
112 static std::string
buildCategoryStr(StringSet
<> &Cetegorys
) {
114 raw_string_ostream
OS(Str
);
115 for (auto &It
: Cetegorys
) {
116 OS
<< " " << It
.getKey();
121 // Emit enum declaration for DXIL.
122 static void emitDXILEnums(std::vector
<DXILOperationData
> &DXILOps
,
124 // Sort by Category + OpName.
125 llvm::sort(DXILOps
, [](DXILOperationData
&A
, DXILOperationData
&B
) {
126 // Group by Category first.
127 if (A
.Category
== B
.Category
)
128 // Inside same Category, order by OpName.
129 return A
.DXILOp
< B
.DXILOp
;
131 return A
.Category
< B
.Category
;
134 OS
<< "// Enumeration for operations specified by DXIL\n";
135 OS
<< "enum class OpCode : unsigned {\n";
137 StringMap
<StringSet
<>> ClassMap
;
138 StringRef PrevCategory
= "";
139 for (auto &DXILOp
: DXILOps
) {
140 StringRef Category
= DXILOp
.Category
;
141 if (Category
!= PrevCategory
) {
142 OS
<< "\n// " << Category
<< "\n";
143 PrevCategory
= Category
;
145 emitDXILOpEnum(DXILOp
, OS
);
146 auto It
= ClassMap
.find(DXILOp
.DXILClass
);
147 if (It
!= ClassMap
.end()) {
148 It
->second
.insert(DXILOp
.Category
);
150 ClassMap
[DXILOp
.DXILClass
].insert(DXILOp
.Category
);
156 std::vector
<std::pair
<std::string
, std::string
>> ClassVec
;
157 for (auto &It
: ClassMap
) {
158 ClassVec
.emplace_back(
159 std::make_pair(It
.getKey().str(), buildCategoryStr(It
.second
)));
161 // Sort by Category + ClassName.
162 llvm::sort(ClassVec
, [](std::pair
<std::string
, std::string
> &A
,
163 std::pair
<std::string
, std::string
> &B
) {
164 StringRef ClassA
= A
.first
;
165 StringRef CategoryA
= A
.second
;
166 StringRef ClassB
= B
.first
;
167 StringRef CategoryB
= B
.second
;
168 // Group by Category first.
169 if (CategoryA
== CategoryB
)
170 // Inside same Category, order by ClassName.
171 return ClassA
< ClassB
;
173 return CategoryA
< CategoryB
;
176 OS
<< "// Groups for DXIL operations with equivalent function templates\n";
177 OS
<< "enum class OpCodeClass : unsigned {\n";
179 for (auto &It
: ClassVec
) {
181 StringRef Category
= It
.second
;
182 if (Category
!= PrevCategory
) {
183 OS
<< "\n// " << Category
<< "\n";
184 PrevCategory
= Category
;
186 StringRef Name
= It
.first
;
192 // Emit map from llvm intrinsic to DXIL operation.
193 static void emitDXILIntrinsicMap(std::vector
<DXILOperationData
> &DXILOps
,
196 // FIXME: use array instead of SmallDenseMap.
197 OS
<< "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = "
199 for (auto &DXILOp
: DXILOps
) {
200 if (DXILOp
.Intrinsic
.empty())
202 // {Intrinsic::sin, DXIL::OpCode::Sin},
203 OS
<< " { Intrinsic::" << DXILOp
.Intrinsic
204 << ", DXIL::OpCode::" << DXILOp
.DXILOp
<< "},\n";
210 static std::string
emitDXILOperationFnAttr(StringRef FnAttr
) {
211 return StringSwitch
<std::string
>(FnAttr
)
212 .Case("rn", "Attribute::ReadNone")
213 .Case("ro", "Attribute::ReadOnly")
214 .Default("Attribute::None");
217 static std::string
getOverloadKind(StringRef Overload
) {
218 return StringSwitch
<std::string
>(Overload
)
219 .Case("half", "OverloadKind::HALF")
220 .Case("float", "OverloadKind::FLOAT")
221 .Case("double", "OverloadKind::DOUBLE")
222 .Case("i1", "OverloadKind::I1")
223 .Case("i16", "OverloadKind::I16")
224 .Case("i32", "OverloadKind::I32")
225 .Case("i64", "OverloadKind::I64")
226 .Case("udt", "OverloadKind::UserDefineType")
227 .Case("obj", "OverloadKind::ObjectType")
228 .Default("OverloadKind::VOID");
231 static std::string
getDXILOperationOverload(StringRef Overloads
) {
232 SmallVector
<StringRef
> OverloadStrs
;
233 Overloads
.split(OverloadStrs
, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false);
234 // Format is: OverloadKind::FLOAT | OverloadKind::HALF
235 assert(!OverloadStrs
.empty() && "Invalid overloads");
236 auto It
= OverloadStrs
.begin();
238 raw_string_ostream
OS(Result
);
239 OS
<< getOverloadKind(*It
);
240 for (++It
; It
!= OverloadStrs
.end(); ++It
) {
241 OS
<< " | " << getOverloadKind(*It
);
246 static std::string
lowerFirstLetter(StringRef Name
) {
250 std::string LowerName
= Name
.str();
251 LowerName
[0] = llvm::toLower(Name
[0]);
255 static std::string
getDXILOpClassName(StringRef DXILOpClass
) {
256 // Lower first letter expect for special case.
257 return StringSwitch
<std::string
>(DXILOpClass
)
258 .Case("CBufferLoad", "cbufferLoad")
259 .Case("CBufferLoadLegacy", "cbufferLoadLegacy")
260 .Case("GSInstanceID", "gsInstanceID")
261 .Default(lowerFirstLetter(DXILOpClass
));
264 static void emitDXILOperationTable(std::vector
<DXILOperationData
> &DXILOps
,
267 llvm::sort(DXILOps
, [](DXILOperationData
&A
, DXILOperationData
&B
) {
268 return A
.DXILOpID
< B
.DXILOpID
;
272 SequenceToOffsetTable
<std::string
> OpClassStrings
;
273 SequenceToOffsetTable
<std::string
> OpStrings
;
275 StringSet
<> ClassSet
;
276 for (auto &DXILOp
: DXILOps
) {
277 OpStrings
.add(DXILOp
.DXILOp
.str());
279 if (ClassSet
.find(DXILOp
.DXILClass
) != ClassSet
.end())
281 ClassSet
.insert(DXILOp
.DXILClass
);
282 OpClassStrings
.add(getDXILOpClassName(DXILOp
.DXILClass
));
287 OpClassStrings
.layout();
289 // Emit the DXIL operation table.
290 //{DXIL::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary,
291 // OpCodeClassNameIndex,
292 // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone},
293 OS
<< "static const OpCodeProperty *getOpCodeProperty(DXIL::OpCode DXILOp) "
296 OS
<< " static const OpCodeProperty OpCodeProps[] = {\n";
297 for (auto &DXILOp
: DXILOps
) {
298 OS
<< " { DXIL::OpCode::" << DXILOp
.DXILOp
<< ", "
299 << OpStrings
.get(DXILOp
.DXILOp
.str())
300 << ", OpCodeClass::" << DXILOp
.DXILClass
<< ", "
301 << OpClassStrings
.get(getDXILOpClassName(DXILOp
.DXILClass
)) << ", "
302 << getDXILOperationOverload(DXILOp
.OverloadTypes
) << ", "
303 << emitDXILOperationFnAttr(DXILOp
.FnAttr
) << " },\n";
307 OS
<< " // FIXME: change search to indexing with\n";
308 OS
<< " // DXILOp once all DXIL op is added.\n";
309 OS
<< " OpCodeProperty TmpProp;\n";
310 OS
<< " TmpProp.OpCode = DXILOp;\n";
311 OS
<< " const OpCodeProperty *Prop =\n";
312 OS
<< " llvm::lower_bound(OpCodeProps, TmpProp,\n";
313 OS
<< " [](const OpCodeProperty &A, const "
314 "OpCodeProperty &B) {\n";
315 OS
<< " return A.OpCode < B.OpCode;\n";
317 OS
<< " assert(Prop && \"fail to find OpCodeProperty\");\n";
318 OS
<< " return Prop;\n";
321 // Emit the string tables.
322 OS
<< "static const char *getOpCodeName(DXIL::OpCode DXILOp) {\n\n";
324 OpStrings
.emitStringLiteralDef(OS
,
325 " static const char DXILOpCodeNameTable[]");
327 OS
<< " auto *Prop = getOpCodeProperty(DXILOp);\n";
328 OS
<< " unsigned Index = Prop->OpCodeNameOffset;\n";
329 OS
<< " return DXILOpCodeNameTable + Index;\n";
332 OS
<< "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
335 OpClassStrings
.emitStringLiteralDef(
336 OS
, " static const char DXILOpCodeClassNameTable[]");
338 OS
<< " unsigned Index = Prop.OpCodeClassNameOffset;\n";
339 OS
<< " return DXILOpCodeClassNameTable + Index;\n";
345 void EmitDXILOperation(RecordKeeper
&Records
, raw_ostream
&OS
) {
346 std::vector
<Record
*> Ops
= Records
.getAllDerivedDefinitions("dxil_op");
347 OS
<< "// Generated code, do not edit.\n";
350 std::vector
<DXILOperationData
> DXILOps
;
351 DXILOps
.reserve(Ops
.size());
352 for (auto *Record
: Ops
) {
353 DXILOps
.emplace_back(DXILOperationData(Record
));
356 OS
<< "#ifdef DXIL_OP_ENUM\n";
357 emitDXILEnums(DXILOps
, OS
);
360 OS
<< "#ifdef DXIL_OP_INTRINSIC_MAP\n";
361 emitDXILIntrinsicMap(DXILOps
, OS
);
364 OS
<< "#ifdef DXIL_OP_OPERATION_TABLE\n";
365 emitDXILOperationTable(DXILOps
, OS
);