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 "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/Error.h"
21 #include "llvm/TableGen/Record.h"
22
23 using namespace llvm;
24 using namespace llvm::dxil;
25
26 namespace {
27
28 struct DXILShaderModel {
29 int Major;
30 int Minor;
31 };
32
33 struct DXILParam {
34 int Pos; // position in parameter list
35 ParameterKind Kind;
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);
42 };
43
44 struct DXILOperationData {
45 StringRef Name; // short, unique name
46
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
52
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
58 // means no map exist
59 bool IsDeriv; // whether this is some kind of derivative
60 bool IsGradient; // whether this requires a gradient calculation
61 bool IsFeedback; // whether this is a sampler feedback op
62 bool IsWave; // whether this requires in-wave, cross-lane functionality
63 bool RequiresUniformInputs; // whether this operation requires that all
64 // of its inputs are uniform across the wave
65 SmallVector<StringRef, 4>
66 ShaderStages; // shader stages to which this applies, empty for all.
67 DXILShaderModel ShaderModel; // minimum shader model required
68 DXILShaderModel ShaderModelTranslated; // minimum shader model required with
69 // translation by linker
70 int OverloadParamIndex; // parameter index which control the overload.
71 // When < 0, should be only 1 overload type.
72 SmallVector<StringRef, 4> counters; // counters for this inst.
DXILOperationData__anon56602f7d0111::DXILOperationData73 DXILOperationData(const Record *R) {
74 Name = R->getValueAsString("name");
75 DXILOp = R->getValueAsString("dxil_op");
76 DXILOpID = R->getValueAsInt("dxil_opid");
77 DXILClass = R->getValueAsDef("op_class")->getValueAsString("name");
78 Category = R->getValueAsDef("category")->getValueAsString("name");
79
80 if (R->getValue("llvm_intrinsic")) {
81 auto *IntrinsicDef = R->getValueAsDef("llvm_intrinsic");
82 auto DefName = IntrinsicDef->getName();
83 assert(DefName.startswith("int_") && "invalid intrinsic name");
84 // Remove the int_ from intrinsic name.
85 Intrinsic = DefName.substr(4);
86 }
87
88 Doc = R->getValueAsString("doc");
89
90 ListInit *ParamList = R->getValueAsListInit("ops");
91 OverloadParamIndex = -1;
92 for (unsigned I = 0; I < ParamList->size(); ++I) {
93 Record *Param = ParamList->getElementAsRecord(I);
94 Params.emplace_back(DXILParam(Param));
95 auto &CurParam = Params.back();
96 if (CurParam.Kind >= ParameterKind::OVERLOAD)
97 OverloadParamIndex = I;
98 }
99 OverloadTypes = R->getValueAsString("oload_types");
100 FnAttr = R->getValueAsString("fn_attr");
101 }
102 };
103 } // end anonymous namespace
104
DXILParam(const Record * R)105 DXILParam::DXILParam(const Record *R) {
106 Name = R->getValueAsString("name");
107 Pos = R->getValueAsInt("pos");
108 Kind = parameterTypeNameToKind(R->getValueAsString("llvm_type"));
109 if (R->getValue("doc"))
110 Doc = R->getValueAsString("doc");
111 IsConst = R->getValueAsBit("is_const");
112 EnumName = R->getValueAsString("enum_name");
113 MaxValue = R->getValueAsInt("max_value");
114 }
115
parameterKindToString(ParameterKind Kind)116 static std::string parameterKindToString(ParameterKind Kind) {
117 switch (Kind) {
118 case ParameterKind::INVALID:
119 return "INVALID";
120 case ParameterKind::VOID:
121 return "VOID";
122 case ParameterKind::HALF:
123 return "HALF";
124 case ParameterKind::FLOAT:
125 return "FLOAT";
126 case ParameterKind::DOUBLE:
127 return "DOUBLE";
128 case ParameterKind::I1:
129 return "I1";
130 case ParameterKind::I8:
131 return "I8";
132 case ParameterKind::I16:
133 return "I16";
134 case ParameterKind::I32:
135 return "I32";
136 case ParameterKind::I64:
137 return "I64";
138 case ParameterKind::OVERLOAD:
139 return "OVERLOAD";
140 case ParameterKind::CBUFFER_RET:
141 return "CBUFFER_RET";
142 case ParameterKind::RESOURCE_RET:
143 return "RESOURCE_RET";
144 case ParameterKind::DXIL_HANDLE:
145 return "DXIL_HANDLE";
146 }
147 llvm_unreachable("Unknown llvm::dxil::ParameterKind enum");
148 }
149
emitDXILOpEnum(DXILOperationData & DXILOp,raw_ostream & OS)150 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) {
151 // Name = ID, // Doc
152 OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
153 << "\n";
154 }
155
buildCategoryStr(StringSet<> & Cetegorys)156 static std::string buildCategoryStr(StringSet<> &Cetegorys) {
157 std::string Str;
158 raw_string_ostream OS(Str);
159 for (auto &It : Cetegorys) {
160 OS << " " << It.getKey();
161 }
162 return OS.str();
163 }
164
165 // Emit enum declaration for DXIL.
emitDXILEnums(std::vector<DXILOperationData> & DXILOps,raw_ostream & OS)166 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
167 raw_ostream &OS) {
168 // Sort by Category + OpName.
169 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) {
170 // Group by Category first.
171 if (A.Category == B.Category)
172 // Inside same Category, order by OpName.
173 return A.DXILOp < B.DXILOp;
174 else
175 return A.Category < B.Category;
176 });
177
178 OS << "// Enumeration for operations specified by DXIL\n";
179 OS << "enum class OpCode : unsigned {\n";
180
181 StringMap<StringSet<>> ClassMap;
182 StringRef PrevCategory = "";
183 for (auto &DXILOp : DXILOps) {
184 StringRef Category = DXILOp.Category;
185 if (Category != PrevCategory) {
186 OS << "\n// " << Category << "\n";
187 PrevCategory = Category;
188 }
189 emitDXILOpEnum(DXILOp, OS);
190 auto It = ClassMap.find(DXILOp.DXILClass);
191 if (It != ClassMap.end()) {
192 It->second.insert(DXILOp.Category);
193 } else {
194 ClassMap[DXILOp.DXILClass].insert(DXILOp.Category);
195 }
196 }
197
198 OS << "\n};\n\n";
199
200 std::vector<std::pair<std::string, std::string>> ClassVec;
201 for (auto &It : ClassMap) {
202 ClassVec.emplace_back(
203 std::make_pair(It.getKey().str(), buildCategoryStr(It.second)));
204 }
205 // Sort by Category + ClassName.
206 llvm::sort(ClassVec, [](std::pair<std::string, std::string> &A,
207 std::pair<std::string, std::string> &B) {
208 StringRef ClassA = A.first;
209 StringRef CategoryA = A.second;
210 StringRef ClassB = B.first;
211 StringRef CategoryB = B.second;
212 // Group by Category first.
213 if (CategoryA == CategoryB)
214 // Inside same Category, order by ClassName.
215 return ClassA < ClassB;
216 else
217 return CategoryA < CategoryB;
218 });
219
220 OS << "// Groups for DXIL operations with equivalent function templates\n";
221 OS << "enum class OpCodeClass : unsigned {\n";
222 PrevCategory = "";
223 for (auto &It : ClassVec) {
224
225 StringRef Category = It.second;
226 if (Category != PrevCategory) {
227 OS << "\n// " << Category << "\n";
228 PrevCategory = Category;
229 }
230 StringRef Name = It.first;
231 OS << Name << ",\n";
232 }
233 OS << "\n};\n\n";
234 }
235
236 // Emit map from llvm intrinsic to DXIL operation.
emitDXILIntrinsicMap(std::vector<DXILOperationData> & DXILOps,raw_ostream & OS)237 static void emitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps,
238 raw_ostream &OS) {
239 OS << "\n";
240 // FIXME: use array instead of SmallDenseMap.
241 OS << "static const SmallDenseMap<Intrinsic::ID, dxil::OpCode> LowerMap = "
242 "{\n";
243 for (auto &DXILOp : DXILOps) {
244 if (DXILOp.Intrinsic.empty())
245 continue;
246 // {Intrinsic::sin, dxil::OpCode::Sin},
247 OS << " { Intrinsic::" << DXILOp.Intrinsic
248 << ", dxil::OpCode::" << DXILOp.DXILOp << "},\n";
249 }
250 OS << "};\n";
251 OS << "\n";
252 }
253
emitDXILOperationFnAttr(StringRef FnAttr)254 static std::string emitDXILOperationFnAttr(StringRef FnAttr) {
255 return StringSwitch<std::string>(FnAttr)
256 .Case("rn", "Attribute::ReadNone")
257 .Case("ro", "Attribute::ReadOnly")
258 .Default("Attribute::None");
259 }
260
getOverloadKind(StringRef Overload)261 static std::string getOverloadKind(StringRef Overload) {
262 return StringSwitch<std::string>(Overload)
263 .Case("half", "OverloadKind::HALF")
264 .Case("float", "OverloadKind::FLOAT")
265 .Case("double", "OverloadKind::DOUBLE")
266 .Case("i1", "OverloadKind::I1")
267 .Case("i16", "OverloadKind::I16")
268 .Case("i32", "OverloadKind::I32")
269 .Case("i64", "OverloadKind::I64")
270 .Case("udt", "OverloadKind::UserDefineType")
271 .Case("obj", "OverloadKind::ObjectType")
272 .Default("OverloadKind::VOID");
273 }
274
getDXILOperationOverload(StringRef Overloads)275 static std::string getDXILOperationOverload(StringRef Overloads) {
276 SmallVector<StringRef> OverloadStrs;
277 Overloads.split(OverloadStrs, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false);
278 // Format is: OverloadKind::FLOAT | OverloadKind::HALF
279 assert(!OverloadStrs.empty() && "Invalid overloads");
280 auto It = OverloadStrs.begin();
281 std::string Result;
282 raw_string_ostream OS(Result);
283 OS << getOverloadKind(*It);
284 for (++It; It != OverloadStrs.end(); ++It) {
285 OS << " | " << getOverloadKind(*It);
286 }
287 return OS.str();
288 }
289
lowerFirstLetter(StringRef Name)290 static std::string lowerFirstLetter(StringRef Name) {
291 if (Name.empty())
292 return "";
293
294 std::string LowerName = Name.str();
295 LowerName[0] = llvm::toLower(Name[0]);
296 return LowerName;
297 }
298
getDXILOpClassName(StringRef DXILOpClass)299 static std::string getDXILOpClassName(StringRef DXILOpClass) {
300 // Lower first letter expect for special case.
301 return StringSwitch<std::string>(DXILOpClass)
302 .Case("CBufferLoad", "cbufferLoad")
303 .Case("CBufferLoadLegacy", "cbufferLoadLegacy")
304 .Case("GSInstanceID", "gsInstanceID")
305 .Default(lowerFirstLetter(DXILOpClass));
306 }
307
emitDXILOperationTable(std::vector<DXILOperationData> & DXILOps,raw_ostream & OS)308 static void emitDXILOperationTable(std::vector<DXILOperationData> &DXILOps,
309 raw_ostream &OS) {
310 // Sort by DXILOpID.
311 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) {
312 return A.DXILOpID < B.DXILOpID;
313 });
314
315 // Collect Names.
316 SequenceToOffsetTable<std::string> OpClassStrings;
317 SequenceToOffsetTable<std::string> OpStrings;
318 SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters;
319
320 StringMap<SmallVector<ParameterKind>> ParameterMap;
321 StringSet<> ClassSet;
322 for (auto &DXILOp : DXILOps) {
323 OpStrings.add(DXILOp.DXILOp.str());
324
325 if (ClassSet.find(DXILOp.DXILClass) != ClassSet.end())
326 continue;
327 ClassSet.insert(DXILOp.DXILClass);
328 OpClassStrings.add(getDXILOpClassName(DXILOp.DXILClass));
329 SmallVector<ParameterKind> ParamKindVec;
330 for (auto &Param : DXILOp.Params) {
331 ParamKindVec.emplace_back(Param.Kind);
332 }
333 ParameterMap[DXILOp.DXILClass] = ParamKindVec;
334 Parameters.add(ParamKindVec);
335 }
336
337 // Layout names.
338 OpStrings.layout();
339 OpClassStrings.layout();
340 Parameters.layout();
341
342 // Emit the DXIL operation table.
343 //{dxil::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary,
344 // OpCodeClassNameIndex,
345 // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0,
346 // 3, ParameterTableOffset},
347 OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode DXILOp) "
348 "{\n";
349
350 OS << " static const OpCodeProperty OpCodeProps[] = {\n";
351 for (auto &DXILOp : DXILOps) {
352 OS << " { dxil::OpCode::" << DXILOp.DXILOp << ", "
353 << OpStrings.get(DXILOp.DXILOp.str())
354 << ", OpCodeClass::" << DXILOp.DXILClass << ", "
355 << OpClassStrings.get(getDXILOpClassName(DXILOp.DXILClass)) << ", "
356 << getDXILOperationOverload(DXILOp.OverloadTypes) << ", "
357 << emitDXILOperationFnAttr(DXILOp.FnAttr) << ", "
358 << DXILOp.OverloadParamIndex << ", " << DXILOp.Params.size() << ", "
359 << Parameters.get(ParameterMap[DXILOp.DXILClass]) << " },\n";
360 }
361 OS << " };\n";
362
363 OS << " // FIXME: change search to indexing with\n";
364 OS << " // DXILOp once all DXIL op is added.\n";
365 OS << " OpCodeProperty TmpProp;\n";
366 OS << " TmpProp.OpCode = DXILOp;\n";
367 OS << " const OpCodeProperty *Prop =\n";
368 OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n";
369 OS << " [](const OpCodeProperty &A, const "
370 "OpCodeProperty &B) {\n";
371 OS << " return A.OpCode < B.OpCode;\n";
372 OS << " });\n";
373 OS << " assert(Prop && \"fail to find OpCodeProperty\");\n";
374 OS << " return Prop;\n";
375 OS << "}\n\n";
376
377 // Emit the string tables.
378 OS << "static const char *getOpCodeName(dxil::OpCode DXILOp) {\n\n";
379
380 OpStrings.emitStringLiteralDef(OS,
381 " static const char DXILOpCodeNameTable[]");
382
383 OS << " auto *Prop = getOpCodeProperty(DXILOp);\n";
384 OS << " unsigned Index = Prop->OpCodeNameOffset;\n";
385 OS << " return DXILOpCodeNameTable + Index;\n";
386 OS << "}\n\n";
387
388 OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
389 "{\n\n";
390
391 OpClassStrings.emitStringLiteralDef(
392 OS, " static const char DXILOpCodeClassNameTable[]");
393
394 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";
395 OS << " return DXILOpCodeClassNameTable + Index;\n";
396 OS << "}\n ";
397
398 OS << "static const ParameterKind *getOpCodeParameterKind(const "
399 "OpCodeProperty &Prop) "
400 "{\n\n";
401 OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n";
402 Parameters.emit(
403 OS,
404 [](raw_ostream &ParamOS, ParameterKind Kind) {
405 ParamOS << "ParameterKind::" << parameterKindToString(Kind);
406 },
407 "ParameterKind::INVALID");
408 OS << " };\n\n";
409 OS << " unsigned Index = Prop.ParameterTableOffset;\n";
410 OS << " return DXILOpParameterKindTable + Index;\n";
411 OS << "}\n ";
412 }
413
414 namespace llvm {
415
EmitDXILOperation(RecordKeeper & Records,raw_ostream & OS)416 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
417 std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op");
418 OS << "// Generated code, do not edit.\n";
419 OS << "\n";
420
421 std::vector<DXILOperationData> DXILOps;
422 DXILOps.reserve(Ops.size());
423 for (auto *Record : Ops) {
424 DXILOps.emplace_back(DXILOperationData(Record));
425 }
426
427 OS << "#ifdef DXIL_OP_ENUM\n";
428 emitDXILEnums(DXILOps, OS);
429 OS << "#endif\n\n";
430
431 OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
432 emitDXILIntrinsicMap(DXILOps, OS);
433 OS << "#endif\n\n";
434
435 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";
436 emitDXILOperationTable(DXILOps, OS);
437 OS << "#endif\n\n";
438
439 OS << "\n";
440 }
441
442 } // namespace llvm
443