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 "llvm/ADT/MapVector.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/TableGen/Error.h"
19 #include "llvm/TableGen/Record.h"
20 
21 using namespace llvm;
22 
23 namespace {
24 
25 struct DXILShaderModel {
26   int Major;
27   int Minor;
28 };
29 struct DXILParam {
30   int Pos;        // position in parameter list
31   StringRef Type; // llvm type name, $o for overload, $r for resource
32                   // type, $cb for legacy cbuffer, $u4 for u4 struct
33   StringRef Name; // short, unique name
34   StringRef Doc;  // the documentation description of this parameter
35   bool IsConst;   // whether this argument requires a constant value in the IR
36   StringRef EnumName; // the name of the enum type if applicable
37   int MaxValue;       // the maximum value for this parameter if applicable
38   DXILParam(const Record *R) {
39     Name = R->getValueAsString("name");
40     Pos = R->getValueAsInt("pos");
41     Type = R->getValueAsString("llvm_type");
42     if (R->getValue("doc"))
43       Doc = R->getValueAsString("doc");
44     IsConst = R->getValueAsBit("is_const");
45     EnumName = R->getValueAsString("enum_name");
46     MaxValue = R->getValueAsInt("max_value");
47   }
48 };
49 
50 struct DXILOperationData {
51   StringRef Name; // short, unique name
52 
53   StringRef DXILOp;    // name of DXIL operation
54   int DXILOpID;        // ID of DXIL operation
55   StringRef DXILClass; // name of the opcode class
56   StringRef Category;  // classification for this instruction
57   StringRef Doc;       // the documentation description of this instruction
58 
59   SmallVector<DXILParam> Params; // the operands that this instruction takes
60   StringRef OverloadTypes;       // overload types if applicable
61   StringRef FnAttr;              // attribute shorthands: rn=does not access
62                                  // memory,ro=only reads from memory
63   StringRef Intrinsic; // The llvm intrinsic map to DXILOp. Default is "" which
64                        // means no map exist
65   bool IsDeriv;        // whether this is some kind of derivative
66   bool IsGradient;               // whether this requires a gradient calculation
67   bool IsFeedback;               // whether this is a sampler feedback op
68   bool IsWave; // whether this requires in-wave, cross-lane functionality
69   bool RequiresUniformInputs; // whether this operation requires that all
70                               // of its inputs are uniform across the wave
71   SmallVector<StringRef, 4>
72       ShaderStages; // shader stages to which this applies, empty for all.
73   DXILShaderModel ShaderModel;           // minimum shader model required
74   DXILShaderModel ShaderModelTranslated; // minimum shader model required with
75                                          // translation by linker
76   SmallVector<StringRef, 4> counters;    // counters for this inst.
77   DXILOperationData(const Record *R) {
78     Name = R->getValueAsString("name");
79     DXILOp = R->getValueAsString("dxil_op");
80     DXILOpID = R->getValueAsInt("dxil_opid");
81     DXILClass = R->getValueAsDef("op_class")->getValueAsString("name");
82     Category = R->getValueAsDef("category")->getValueAsString("name");
83 
84     if (R->getValue("llvm_intrinsic")) {
85       auto *IntrinsicDef = R->getValueAsDef("llvm_intrinsic");
86       auto DefName = IntrinsicDef->getName();
87       assert(DefName.startswith("int_") && "invalid intrinsic name");
88       // Remove the int_ from intrinsic name.
89       Intrinsic = DefName.substr(4);
90     }
91 
92     Doc = R->getValueAsString("doc");
93 
94     ListInit *ParamList = R->getValueAsListInit("ops");
95     for (unsigned i = 0; i < ParamList->size(); ++i) {
96       Record *Param = ParamList->getElementAsRecord(i);
97       Params.emplace_back(DXILParam(Param));
98     }
99     OverloadTypes = R->getValueAsString("oload_types");
100     FnAttr = R->getValueAsString("fn_attr");
101   }
102 };
103 } // end anonymous namespace
104 
105 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) {
106   // Name = ID, // Doc
107   OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
108      << "\n";
109 }
110 
111 static std::string buildCategoryStr(StringSet<> &Cetegorys) {
112   std::string Str;
113   raw_string_ostream OS(Str);
114   for (auto &It : Cetegorys) {
115     OS << " " << It.getKey();
116   }
117   return OS.str();
118 }
119 
120 // Emit enum declaration for DXIL.
121 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
122                           raw_ostream &OS) {
123   // Sort by Category + OpName.
124   std::sort(DXILOps.begin(), DXILOps.end(),
125             [](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;
132             });
133 
134   OS << "// Enumeration for operations specified by DXIL\n";
135   OS << "enum class OpCode : unsigned {\n";
136 
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;
144     }
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);
151     }
152   }
153 
154   OS << "\n};\n\n";
155 
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)));
160   }
161   // Sort by Category + ClassName.
162   std::sort(ClassVec.begin(), ClassVec.end(),
163             [](std::pair<std::string, std::string> &A,
164                std::pair<std::string, std::string> &B) {
165               StringRef ClassA = A.first;
166               StringRef CategoryA = A.second;
167               StringRef ClassB = B.first;
168               StringRef CategoryB = B.second;
169               // Group by Category first.
170               if (CategoryA == CategoryB)
171                 // Inside same Category, order by ClassName.
172                 return ClassA < ClassB;
173               else
174                 return CategoryA < CategoryB;
175             });
176 
177   OS << "// Groups for DXIL operations with equivalent function templates\n";
178   OS << "enum class OpCodeClass : unsigned {\n";
179   PrevCategory = "";
180   for (auto &It : ClassVec) {
181 
182     StringRef Category = It.second;
183     if (Category != PrevCategory) {
184       OS << "\n// " << Category << "\n";
185       PrevCategory = Category;
186     }
187     StringRef Name = It.first;
188     OS << Name << ",\n";
189   }
190   OS << "\n};\n\n";
191 }
192 
193 // Emit map from llvm intrinsic to DXIL operation.
194 static void EmitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps,
195                                  raw_ostream &OS) {
196   OS << "\n";
197   // FIXME: use array instead of SmallDenseMap.
198   OS << "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = "
199         "{\n";
200   for (auto &DXILOp : DXILOps) {
201     if (DXILOp.Intrinsic.empty())
202       continue;
203     // {Intrinsic::sin, DXIL::OpCode::Sin},
204     OS << "  { Intrinsic::" << DXILOp.Intrinsic
205        << ", DXIL::OpCode::" << DXILOp.DXILOp << "},\n";
206   }
207   OS << "};\n";
208   OS << "\n";
209 }
210 
211 namespace llvm {
212 
213 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
214   std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op");
215   OS << "// Generated code, do not edit.\n";
216   OS << "\n";
217 
218   std::vector<DXILOperationData> DXILOps;
219   DXILOps.reserve(Ops.size());
220   for (auto *Record : Ops) {
221     DXILOps.emplace_back(DXILOperationData(Record));
222   }
223 
224   OS << "#ifdef DXIL_OP_ENUM\n";
225   emitDXILEnums(DXILOps, OS);
226   OS << "#endif\n\n";
227 
228   OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
229   EmitDXILIntrinsicMap(DXILOps, OS);
230   OS << "#endif\n\n";
231 
232 
233   OS << "\n";
234 }
235 
236 } // namespace llvm
237