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   bool IsDeriv;                  // whether this is some kind of derivative
64   bool IsGradient;               // whether this requires a gradient calculation
65   bool IsFeedback;               // whether this is a sampler feedback op
66   bool IsWave; // whether this requires in-wave, cross-lane functionality
67   bool RequiresUniformInputs; // whether this operation requires that all
68                               // of its inputs are uniform across the wave
69   SmallVector<StringRef, 4>
70       ShaderStages; // shader stages to which this applies, empty for all.
71   DXILShaderModel ShaderModel;           // minimum shader model required
72   DXILShaderModel ShaderModelTranslated; // minimum shader model required with
73                                          // translation by linker
74   SmallVector<StringRef, 4> counters;    // counters for this inst.
75   DXILOperationData(const Record *R) {
76     Name = R->getValueAsString("name");
77     DXILOp = R->getValueAsString("dxil_op");
78     DXILOpID = R->getValueAsInt("dxil_opid");
79     DXILClass = R->getValueAsDef("op_class")->getValueAsString("name");
80     Category = R->getValueAsDef("category")->getValueAsString("name");
81 
82     Doc = R->getValueAsString("doc");
83     ListInit *ParamList = R->getValueAsListInit("ops");
84     for (unsigned i = 0; i < ParamList->size(); ++i) {
85       Record *Param = ParamList->getElementAsRecord(i);
86       Params.emplace_back(DXILParam(Param));
87     }
88     OverloadTypes = R->getValueAsString("oload_types");
89     FnAttr = R->getValueAsString("fn_attr");
90   }
91 };
92 } // end anonymous namespace
93 
94 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) {
95   // Name = ID, // Doc
96   OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
97      << "\n";
98 }
99 
100 static std::string buildCategoryStr(StringSet<> &Cetegorys) {
101   std::string Str;
102   raw_string_ostream OS(Str);
103   for (auto &It : Cetegorys) {
104     OS << " " << It.getKey();
105   }
106   return OS.str();
107 }
108 
109 // Emit enum declaration for DXIL.
110 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
111                           raw_ostream &OS) {
112   // Sort by Category + OpName.
113   std::sort(DXILOps.begin(), DXILOps.end(),
114             [](DXILOperationData &A, DXILOperationData &B) {
115               // Group by Category first.
116               if (A.Category == B.Category)
117                 // Inside same Category, order by OpName.
118                 return A.DXILOp < B.DXILOp;
119               else
120                 return A.Category < B.Category;
121             });
122 
123   OS << "// Enumeration for operations specified by DXIL\n";
124   OS << "enum class OpCode : unsigned {\n";
125 
126   StringMap<StringSet<>> ClassMap;
127   StringRef PrevCategory = "";
128   for (auto &DXILOp : DXILOps) {
129     StringRef Category = DXILOp.Category;
130     if (Category != PrevCategory) {
131       OS << "\n// " << Category << "\n";
132       PrevCategory = Category;
133     }
134     emitDXILOpEnum(DXILOp, OS);
135     auto It = ClassMap.find(DXILOp.DXILClass);
136     if (It != ClassMap.end()) {
137       It->second.insert(DXILOp.Category);
138     } else {
139       ClassMap[DXILOp.DXILClass].insert(DXILOp.Category);
140     }
141   }
142 
143   OS << "\n};\n\n";
144 
145   std::vector<std::pair<std::string, std::string>> ClassVec;
146   for (auto &It : ClassMap) {
147     ClassVec.emplace_back(
148         std::make_pair(It.getKey().str(), buildCategoryStr(It.second)));
149   }
150   // Sort by Category + ClassName.
151   std::sort(ClassVec.begin(), ClassVec.end(),
152             [](std::pair<std::string, std::string> &A,
153                std::pair<std::string, std::string> &B) {
154               StringRef ClassA = A.first;
155               StringRef CategoryA = A.second;
156               StringRef ClassB = B.first;
157               StringRef CategoryB = B.second;
158               // Group by Category first.
159               if (CategoryA == CategoryB)
160                 // Inside same Category, order by ClassName.
161                 return ClassA < ClassB;
162               else
163                 return CategoryA < CategoryB;
164             });
165 
166   OS << "// Groups for DXIL operations with equivalent function templates\n";
167   OS << "enum class OpCodeClass : unsigned {\n";
168   PrevCategory = "";
169   for (auto &It : ClassVec) {
170 
171     StringRef Category = It.second;
172     if (Category != PrevCategory) {
173       OS << "\n// " << Category << "\n";
174       PrevCategory = Category;
175     }
176     StringRef Name = It.first;
177     OS << Name << ",\n";
178   }
179   OS << "\n};\n\n";
180 }
181 
182 namespace llvm {
183 
184 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
185   std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op");
186   OS << "// Generated code, do not edit.\n";
187   OS << "\n";
188 
189   std::vector<DXILOperationData> DXILOps;
190   DXILOps.reserve(Ops.size());
191   for (auto *Record : Ops) {
192     DXILOps.emplace_back(DXILOperationData(Record));
193   }
194 
195   OS << "#ifdef DXIL_OP_ENUM\n";
196   emitDXILEnums(DXILOps, OS);
197   OS << "#endif\n\n";
198 
199   OS << "\n";
200 }
201 
202 } // namespace llvm
203