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