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