Use llvm::sort instead of std::sort where possible
[llvm-project.git] / llvm / utils / TableGen / DXILEmitter.cpp
blobb9c563c62bbec30dae344009836729f8f1657da1
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.
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"
22 using namespace llvm;
24 namespace {
26 struct DXILShaderModel {
27 int Major;
28 int Minor;
30 struct DXILParam {
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
65 // means no map exist
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) {
107 // Name = ID, // Doc
108 OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
109 << "\n";
112 static std::string buildCategoryStr(StringSet<> &Cetegorys) {
113 std::string Str;
114 raw_string_ostream OS(Str);
115 for (auto &It : Cetegorys) {
116 OS << " " << It.getKey();
118 return OS.str();
121 // Emit enum declaration for DXIL.
122 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
123 raw_ostream &OS) {
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;
130 else
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);
149 } else {
150 ClassMap[DXILOp.DXILClass].insert(DXILOp.Category);
154 OS << "\n};\n\n";
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;
172 else
173 return CategoryA < CategoryB;
176 OS << "// Groups for DXIL operations with equivalent function templates\n";
177 OS << "enum class OpCodeClass : unsigned {\n";
178 PrevCategory = "";
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;
187 OS << Name << ",\n";
189 OS << "\n};\n\n";
192 // Emit map from llvm intrinsic to DXIL operation.
193 static void emitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps,
194 raw_ostream &OS) {
195 OS << "\n";
196 // FIXME: use array instead of SmallDenseMap.
197 OS << "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = "
198 "{\n";
199 for (auto &DXILOp : DXILOps) {
200 if (DXILOp.Intrinsic.empty())
201 continue;
202 // {Intrinsic::sin, DXIL::OpCode::Sin},
203 OS << " { Intrinsic::" << DXILOp.Intrinsic
204 << ", DXIL::OpCode::" << DXILOp.DXILOp << "},\n";
206 OS << "};\n";
207 OS << "\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();
237 std::string Result;
238 raw_string_ostream OS(Result);
239 OS << getOverloadKind(*It);
240 for (++It; It != OverloadStrs.end(); ++It) {
241 OS << " | " << getOverloadKind(*It);
243 return OS.str();
246 static std::string lowerFirstLetter(StringRef Name) {
247 if (Name.empty())
248 return "";
250 std::string LowerName = Name.str();
251 LowerName[0] = llvm::toLower(Name[0]);
252 return LowerName;
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,
265 raw_ostream &OS) {
266 // Sort by DXILOpID.
267 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) {
268 return A.DXILOpID < B.DXILOpID;
271 // Collect Names.
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())
280 continue;
281 ClassSet.insert(DXILOp.DXILClass);
282 OpClassStrings.add(getDXILOpClassName(DXILOp.DXILClass));
285 // Layout names.
286 OpStrings.layout();
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) "
294 "{\n";
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";
305 OS << " };\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";
316 OS << " });\n";
317 OS << " assert(Prop && \"fail to find OpCodeProperty\");\n";
318 OS << " return Prop;\n";
319 OS << "}\n\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";
330 OS << "}\n\n";
332 OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
333 "{\n\n";
335 OpClassStrings.emitStringLiteralDef(
336 OS, " static const char DXILOpCodeClassNameTable[]");
338 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";
339 OS << " return DXILOpCodeClassNameTable + Index;\n";
340 OS << "}\n ";
343 namespace llvm {
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";
348 OS << "\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);
358 OS << "#endif\n\n";
360 OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
361 emitDXILIntrinsicMap(DXILOps, OS);
362 OS << "#endif\n\n";
364 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";
365 emitDXILOperationTable(DXILOps, OS);
366 OS << "#endif\n\n";
368 OS << "\n";
371 } // namespace llvm