xref: /freebsd-src/contrib/llvm-project/llvm/utils/TableGen/DXILEmitter.cpp (revision 753f127f3ace09432b2baeffd71a308760641a62)
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/TableGen/Error.h"
20 #include "llvm/TableGen/Record.h"
21 
22 using namespace llvm;
23 
24 namespace {
25 
26 struct DXILShaderModel {
27   int Major;
28   int Minor;
29 };
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");
48   }
49 };
50 
51 struct DXILOperationData {
52   StringRef Name; // short, unique name
53 
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
59 
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");
84 
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);
91     }
92 
93     Doc = R->getValueAsString("doc");
94 
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));
99     }
100     OverloadTypes = R->getValueAsString("oload_types");
101     FnAttr = R->getValueAsString("fn_attr");
102   }
103 };
104 } // end anonymous namespace
105 
106 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) {
107   // Name = ID, // Doc
108   OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
109      << "\n";
110 }
111 
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();
117   }
118   return OS.str();
119 }
120 
121 // Emit enum declaration for DXIL.
122 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
123                           raw_ostream &OS) {
124   // Sort by Category + OpName.
125   std::sort(DXILOps.begin(), DXILOps.end(),
126             [](DXILOperationData &A, DXILOperationData &B) {
127               // Group by Category first.
128               if (A.Category == B.Category)
129                 // Inside same Category, order by OpName.
130                 return A.DXILOp < B.DXILOp;
131               else
132                 return A.Category < B.Category;
133             });
134 
135   OS << "// Enumeration for operations specified by DXIL\n";
136   OS << "enum class OpCode : unsigned {\n";
137 
138   StringMap<StringSet<>> ClassMap;
139   StringRef PrevCategory = "";
140   for (auto &DXILOp : DXILOps) {
141     StringRef Category = DXILOp.Category;
142     if (Category != PrevCategory) {
143       OS << "\n// " << Category << "\n";
144       PrevCategory = Category;
145     }
146     emitDXILOpEnum(DXILOp, OS);
147     auto It = ClassMap.find(DXILOp.DXILClass);
148     if (It != ClassMap.end()) {
149       It->second.insert(DXILOp.Category);
150     } else {
151       ClassMap[DXILOp.DXILClass].insert(DXILOp.Category);
152     }
153   }
154 
155   OS << "\n};\n\n";
156 
157   std::vector<std::pair<std::string, std::string>> ClassVec;
158   for (auto &It : ClassMap) {
159     ClassVec.emplace_back(
160         std::make_pair(It.getKey().str(), buildCategoryStr(It.second)));
161   }
162   // Sort by Category + ClassName.
163   std::sort(ClassVec.begin(), ClassVec.end(),
164             [](std::pair<std::string, std::string> &A,
165                std::pair<std::string, std::string> &B) {
166               StringRef ClassA = A.first;
167               StringRef CategoryA = A.second;
168               StringRef ClassB = B.first;
169               StringRef CategoryB = B.second;
170               // Group by Category first.
171               if (CategoryA == CategoryB)
172                 // Inside same Category, order by ClassName.
173                 return ClassA < ClassB;
174               else
175                 return CategoryA < CategoryB;
176             });
177 
178   OS << "// Groups for DXIL operations with equivalent function templates\n";
179   OS << "enum class OpCodeClass : unsigned {\n";
180   PrevCategory = "";
181   for (auto &It : ClassVec) {
182 
183     StringRef Category = It.second;
184     if (Category != PrevCategory) {
185       OS << "\n// " << Category << "\n";
186       PrevCategory = Category;
187     }
188     StringRef Name = It.first;
189     OS << Name << ",\n";
190   }
191   OS << "\n};\n\n";
192 }
193 
194 // Emit map from llvm intrinsic to DXIL operation.
195 static void emitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps,
196                                  raw_ostream &OS) {
197   OS << "\n";
198   // FIXME: use array instead of SmallDenseMap.
199   OS << "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = "
200         "{\n";
201   for (auto &DXILOp : DXILOps) {
202     if (DXILOp.Intrinsic.empty())
203       continue;
204     // {Intrinsic::sin, DXIL::OpCode::Sin},
205     OS << "  { Intrinsic::" << DXILOp.Intrinsic
206        << ", DXIL::OpCode::" << DXILOp.DXILOp << "},\n";
207   }
208   OS << "};\n";
209   OS << "\n";
210 }
211 
212 static std::string emitDXILOperationFnAttr(StringRef FnAttr) {
213   return StringSwitch<std::string>(FnAttr)
214       .Case("rn", "Attribute::ReadNone")
215       .Case("ro", "Attribute::ReadOnly")
216       .Default("Attribute::None");
217 }
218 
219 static std::string getOverloadKind(StringRef Overload) {
220   return StringSwitch<std::string>(Overload)
221       .Case("half", "OverloadKind::HALF")
222       .Case("float", "OverloadKind::FLOAT")
223       .Case("double", "OverloadKind::DOUBLE")
224       .Case("i1", "OverloadKind::I1")
225       .Case("i16", "OverloadKind::I16")
226       .Case("i32", "OverloadKind::I32")
227       .Case("i64", "OverloadKind::I64")
228       .Case("udt", "OverloadKind::UserDefineType")
229       .Case("obj", "OverloadKind::ObjectType")
230       .Default("OverloadKind::VOID");
231 }
232 
233 static std::string getDXILOperationOverload(StringRef Overloads) {
234   SmallVector<StringRef> OverloadStrs;
235   Overloads.split(OverloadStrs, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false);
236   // Format is: OverloadKind::FLOAT | OverloadKind::HALF
237   assert(!OverloadStrs.empty() && "Invalid overloads");
238   auto It = OverloadStrs.begin();
239   std::string Result;
240   raw_string_ostream OS(Result);
241   OS << getOverloadKind(*It);
242   for (++It; It != OverloadStrs.end(); ++It) {
243     OS << " | " << getOverloadKind(*It);
244   }
245   return OS.str();
246 }
247 
248 static std::string lowerFirstLetter(StringRef Name) {
249   if (Name.empty())
250     return "";
251 
252   std::string LowerName = Name.str();
253   LowerName[0] = llvm::toLower(Name[0]);
254   return LowerName;
255 }
256 
257 static std::string getDXILOpClassName(StringRef DXILOpClass) {
258   // Lower first letter expect for special case.
259   return StringSwitch<std::string>(DXILOpClass)
260       .Case("CBufferLoad", "cbufferLoad")
261       .Case("CBufferLoadLegacy", "cbufferLoadLegacy")
262       .Case("GSInstanceID", "gsInstanceID")
263       .Default(lowerFirstLetter(DXILOpClass));
264 }
265 
266 static void emitDXILOperationTable(std::vector<DXILOperationData> &DXILOps,
267                                    raw_ostream &OS) {
268   // Sort by DXILOpID.
269   std::sort(DXILOps.begin(), DXILOps.end(),
270             [](DXILOperationData &A, DXILOperationData &B) {
271               return A.DXILOpID < B.DXILOpID;
272             });
273 
274   // Collect Names.
275   SequenceToOffsetTable<std::string> OpClassStrings;
276   SequenceToOffsetTable<std::string> OpStrings;
277 
278   StringSet<> ClassSet;
279   for (auto &DXILOp : DXILOps) {
280     OpStrings.add(DXILOp.DXILOp.str());
281 
282     if (ClassSet.find(DXILOp.DXILClass) != ClassSet.end())
283       continue;
284     ClassSet.insert(DXILOp.DXILClass);
285     OpClassStrings.add(getDXILOpClassName(DXILOp.DXILClass));
286   }
287 
288   // Layout names.
289   OpStrings.layout();
290   OpClassStrings.layout();
291 
292   // Emit the DXIL operation table.
293   //{DXIL::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary,
294   // OpCodeClassNameIndex,
295   // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone},
296   OS << "static const OpCodeProperty *getOpCodeProperty(DXIL::OpCode DXILOp) "
297         "{\n";
298 
299   OS << "  static const OpCodeProperty OpCodeProps[] = {\n";
300   for (auto &DXILOp : DXILOps) {
301     OS << "  { DXIL::OpCode::" << DXILOp.DXILOp << ", "
302        << OpStrings.get(DXILOp.DXILOp.str())
303        << ", OpCodeClass::" << DXILOp.DXILClass << ", "
304        << OpClassStrings.get(getDXILOpClassName(DXILOp.DXILClass)) << ", "
305        << getDXILOperationOverload(DXILOp.OverloadTypes) << ", "
306        << emitDXILOperationFnAttr(DXILOp.FnAttr) << " },\n";
307   }
308   OS << "  };\n";
309 
310   OS << "  // FIXME: change search to indexing with\n";
311   OS << "  // DXILOp once all DXIL op is added.\n";
312   OS << "  OpCodeProperty TmpProp;\n";
313   OS << "  TmpProp.OpCode = DXILOp;\n";
314   OS << "  const OpCodeProperty *Prop =\n";
315   OS << "      llvm::lower_bound(OpCodeProps, TmpProp,\n";
316   OS << "                        [](const OpCodeProperty &A, const "
317         "OpCodeProperty &B) {\n";
318   OS << "                          return A.OpCode < B.OpCode;\n";
319   OS << "                        });\n";
320   OS << "  assert(Prop && \"fail to find OpCodeProperty\");\n";
321   OS << "  return Prop;\n";
322   OS << "}\n\n";
323 
324   // Emit the string tables.
325   OS << "static const char *getOpCodeName(DXIL::OpCode DXILOp) {\n\n";
326 
327   OpStrings.emitStringLiteralDef(OS,
328                                  "  static const char DXILOpCodeNameTable[]");
329 
330   OS << "  auto *Prop = getOpCodeProperty(DXILOp);\n";
331   OS << "  unsigned Index = Prop->OpCodeNameOffset;\n";
332   OS << "  return DXILOpCodeNameTable + Index;\n";
333   OS << "}\n\n";
334 
335   OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
336         "{\n\n";
337 
338   OpClassStrings.emitStringLiteralDef(
339       OS, "  static const char DXILOpCodeClassNameTable[]");
340 
341   OS << "  unsigned Index = Prop.OpCodeClassNameOffset;\n";
342   OS << "  return DXILOpCodeClassNameTable + Index;\n";
343   OS << "}\n ";
344 }
345 
346 namespace llvm {
347 
348 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
349   std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op");
350   OS << "// Generated code, do not edit.\n";
351   OS << "\n";
352 
353   std::vector<DXILOperationData> DXILOps;
354   DXILOps.reserve(Ops.size());
355   for (auto *Record : Ops) {
356     DXILOps.emplace_back(DXILOperationData(Record));
357   }
358 
359   OS << "#ifdef DXIL_OP_ENUM\n";
360   emitDXILEnums(DXILOps, OS);
361   OS << "#endif\n\n";
362 
363   OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
364   emitDXILIntrinsicMap(DXILOps, OS);
365   OS << "#endif\n\n";
366 
367   OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";
368   emitDXILOperationTable(DXILOps, OS);
369   OS << "#endif\n\n";
370 
371   OS << "\n";
372 }
373 
374 } // namespace llvm
375