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/Support/DXILOperationCommon.h" 20 #include "llvm/TableGen/Error.h" 21 #include "llvm/TableGen/Record.h" 22 23 using namespace llvm; 24 using namespace llvm::DXIL; 25 26 namespace { 27 28 struct DXILShaderModel { 29 int Major; 30 int Minor; 31 }; 32 33 struct DXILParam { 34 int Pos; // position in parameter list 35 ParameterKind Kind; 36 StringRef Name; // short, unique name 37 StringRef Doc; // the documentation description of this parameter 38 bool IsConst; // whether this argument requires a constant value in the IR 39 StringRef EnumName; // the name of the enum type if applicable 40 int MaxValue; // the maximum value for this parameter if applicable 41 DXILParam(const Record *R); 42 }; 43 44 struct DXILOperationData { 45 StringRef Name; // short, unique name 46 47 StringRef DXILOp; // name of DXIL operation 48 int DXILOpID; // ID of DXIL operation 49 StringRef DXILClass; // name of the opcode class 50 StringRef Category; // classification for this instruction 51 StringRef Doc; // the documentation description of this instruction 52 53 SmallVector<DXILParam> Params; // the operands that this instruction takes 54 StringRef OverloadTypes; // overload types if applicable 55 StringRef FnAttr; // attribute shorthands: rn=does not access 56 // memory,ro=only reads from memory 57 StringRef Intrinsic; // The llvm intrinsic map to DXILOp. Default is "" which 58 // means no map exist 59 bool IsDeriv; // whether this is some kind of derivative 60 bool IsGradient; // whether this requires a gradient calculation 61 bool IsFeedback; // whether this is a sampler feedback op 62 bool IsWave; // whether this requires in-wave, cross-lane functionality 63 bool RequiresUniformInputs; // whether this operation requires that all 64 // of its inputs are uniform across the wave 65 SmallVector<StringRef, 4> 66 ShaderStages; // shader stages to which this applies, empty for all. 67 DXILShaderModel ShaderModel; // minimum shader model required 68 DXILShaderModel ShaderModelTranslated; // minimum shader model required with 69 // translation by linker 70 int OverloadParamIndex; // parameter index which control the overload. 71 // When < 0, should be only 1 overload type. 72 SmallVector<StringRef, 4> counters; // counters for this inst. 73 DXILOperationData(const Record *R) { 74 Name = R->getValueAsString("name"); 75 DXILOp = R->getValueAsString("dxil_op"); 76 DXILOpID = R->getValueAsInt("dxil_opid"); 77 DXILClass = R->getValueAsDef("op_class")->getValueAsString("name"); 78 Category = R->getValueAsDef("category")->getValueAsString("name"); 79 80 if (R->getValue("llvm_intrinsic")) { 81 auto *IntrinsicDef = R->getValueAsDef("llvm_intrinsic"); 82 auto DefName = IntrinsicDef->getName(); 83 assert(DefName.startswith("int_") && "invalid intrinsic name"); 84 // Remove the int_ from intrinsic name. 85 Intrinsic = DefName.substr(4); 86 } 87 88 Doc = R->getValueAsString("doc"); 89 90 ListInit *ParamList = R->getValueAsListInit("ops"); 91 OverloadParamIndex = -1; 92 for (unsigned I = 0; I < ParamList->size(); ++I) { 93 Record *Param = ParamList->getElementAsRecord(I); 94 Params.emplace_back(DXILParam(Param)); 95 auto &CurParam = Params.back(); 96 if (CurParam.Kind >= ParameterKind::OVERLOAD) 97 OverloadParamIndex = I; 98 } 99 OverloadTypes = R->getValueAsString("oload_types"); 100 FnAttr = R->getValueAsString("fn_attr"); 101 } 102 }; 103 } // end anonymous namespace 104 105 DXILParam::DXILParam(const Record *R) { 106 Name = R->getValueAsString("name"); 107 Pos = R->getValueAsInt("pos"); 108 Kind = parameterTypeNameToKind(R->getValueAsString("llvm_type")); 109 if (R->getValue("doc")) 110 Doc = R->getValueAsString("doc"); 111 IsConst = R->getValueAsBit("is_const"); 112 EnumName = R->getValueAsString("enum_name"); 113 MaxValue = R->getValueAsInt("max_value"); 114 } 115 116 static std::string parameterKindToString(ParameterKind Kind) { 117 switch (Kind) { 118 case ParameterKind::INVALID: 119 return "INVALID"; 120 case ParameterKind::VOID: 121 return "VOID"; 122 case ParameterKind::HALF: 123 return "HALF"; 124 case ParameterKind::FLOAT: 125 return "FLOAT"; 126 case ParameterKind::DOUBLE: 127 return "DOUBLE"; 128 case ParameterKind::I1: 129 return "I1"; 130 case ParameterKind::I8: 131 return "I8"; 132 case ParameterKind::I16: 133 return "I16"; 134 case ParameterKind::I32: 135 return "I32"; 136 case ParameterKind::I64: 137 return "I64"; 138 case ParameterKind::OVERLOAD: 139 return "OVERLOAD"; 140 case ParameterKind::CBUFFER_RET: 141 return "CBUFFER_RET"; 142 case ParameterKind::RESOURCE_RET: 143 return "RESOURCE_RET"; 144 case ParameterKind::DXIL_HANDLE: 145 return "DXIL_HANDLE"; 146 } 147 } 148 149 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) { 150 // Name = ID, // Doc 151 OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc 152 << "\n"; 153 } 154 155 static std::string buildCategoryStr(StringSet<> &Cetegorys) { 156 std::string Str; 157 raw_string_ostream OS(Str); 158 for (auto &It : Cetegorys) { 159 OS << " " << It.getKey(); 160 } 161 return OS.str(); 162 } 163 164 // Emit enum declaration for DXIL. 165 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps, 166 raw_ostream &OS) { 167 // Sort by Category + OpName. 168 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) { 169 // Group by Category first. 170 if (A.Category == B.Category) 171 // Inside same Category, order by OpName. 172 return A.DXILOp < B.DXILOp; 173 else 174 return A.Category < B.Category; 175 }); 176 177 OS << "// Enumeration for operations specified by DXIL\n"; 178 OS << "enum class OpCode : unsigned {\n"; 179 180 StringMap<StringSet<>> ClassMap; 181 StringRef PrevCategory = ""; 182 for (auto &DXILOp : DXILOps) { 183 StringRef Category = DXILOp.Category; 184 if (Category != PrevCategory) { 185 OS << "\n// " << Category << "\n"; 186 PrevCategory = Category; 187 } 188 emitDXILOpEnum(DXILOp, OS); 189 auto It = ClassMap.find(DXILOp.DXILClass); 190 if (It != ClassMap.end()) { 191 It->second.insert(DXILOp.Category); 192 } else { 193 ClassMap[DXILOp.DXILClass].insert(DXILOp.Category); 194 } 195 } 196 197 OS << "\n};\n\n"; 198 199 std::vector<std::pair<std::string, std::string>> ClassVec; 200 for (auto &It : ClassMap) { 201 ClassVec.emplace_back( 202 std::make_pair(It.getKey().str(), buildCategoryStr(It.second))); 203 } 204 // Sort by Category + ClassName. 205 llvm::sort(ClassVec, [](std::pair<std::string, std::string> &A, 206 std::pair<std::string, std::string> &B) { 207 StringRef ClassA = A.first; 208 StringRef CategoryA = A.second; 209 StringRef ClassB = B.first; 210 StringRef CategoryB = B.second; 211 // Group by Category first. 212 if (CategoryA == CategoryB) 213 // Inside same Category, order by ClassName. 214 return ClassA < ClassB; 215 else 216 return CategoryA < CategoryB; 217 }); 218 219 OS << "// Groups for DXIL operations with equivalent function templates\n"; 220 OS << "enum class OpCodeClass : unsigned {\n"; 221 PrevCategory = ""; 222 for (auto &It : ClassVec) { 223 224 StringRef Category = It.second; 225 if (Category != PrevCategory) { 226 OS << "\n// " << Category << "\n"; 227 PrevCategory = Category; 228 } 229 StringRef Name = It.first; 230 OS << Name << ",\n"; 231 } 232 OS << "\n};\n\n"; 233 } 234 235 // Emit map from llvm intrinsic to DXIL operation. 236 static void emitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps, 237 raw_ostream &OS) { 238 OS << "\n"; 239 // FIXME: use array instead of SmallDenseMap. 240 OS << "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = " 241 "{\n"; 242 for (auto &DXILOp : DXILOps) { 243 if (DXILOp.Intrinsic.empty()) 244 continue; 245 // {Intrinsic::sin, DXIL::OpCode::Sin}, 246 OS << " { Intrinsic::" << DXILOp.Intrinsic 247 << ", DXIL::OpCode::" << DXILOp.DXILOp << "},\n"; 248 } 249 OS << "};\n"; 250 OS << "\n"; 251 } 252 253 static std::string emitDXILOperationFnAttr(StringRef FnAttr) { 254 return StringSwitch<std::string>(FnAttr) 255 .Case("rn", "Attribute::ReadNone") 256 .Case("ro", "Attribute::ReadOnly") 257 .Default("Attribute::None"); 258 } 259 260 static std::string getOverloadKind(StringRef Overload) { 261 return StringSwitch<std::string>(Overload) 262 .Case("half", "OverloadKind::HALF") 263 .Case("float", "OverloadKind::FLOAT") 264 .Case("double", "OverloadKind::DOUBLE") 265 .Case("i1", "OverloadKind::I1") 266 .Case("i16", "OverloadKind::I16") 267 .Case("i32", "OverloadKind::I32") 268 .Case("i64", "OverloadKind::I64") 269 .Case("udt", "OverloadKind::UserDefineType") 270 .Case("obj", "OverloadKind::ObjectType") 271 .Default("OverloadKind::VOID"); 272 } 273 274 static std::string getDXILOperationOverload(StringRef Overloads) { 275 SmallVector<StringRef> OverloadStrs; 276 Overloads.split(OverloadStrs, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false); 277 // Format is: OverloadKind::FLOAT | OverloadKind::HALF 278 assert(!OverloadStrs.empty() && "Invalid overloads"); 279 auto It = OverloadStrs.begin(); 280 std::string Result; 281 raw_string_ostream OS(Result); 282 OS << getOverloadKind(*It); 283 for (++It; It != OverloadStrs.end(); ++It) { 284 OS << " | " << getOverloadKind(*It); 285 } 286 return OS.str(); 287 } 288 289 static std::string lowerFirstLetter(StringRef Name) { 290 if (Name.empty()) 291 return ""; 292 293 std::string LowerName = Name.str(); 294 LowerName[0] = llvm::toLower(Name[0]); 295 return LowerName; 296 } 297 298 static std::string getDXILOpClassName(StringRef DXILOpClass) { 299 // Lower first letter expect for special case. 300 return StringSwitch<std::string>(DXILOpClass) 301 .Case("CBufferLoad", "cbufferLoad") 302 .Case("CBufferLoadLegacy", "cbufferLoadLegacy") 303 .Case("GSInstanceID", "gsInstanceID") 304 .Default(lowerFirstLetter(DXILOpClass)); 305 } 306 307 static void emitDXILOperationTable(std::vector<DXILOperationData> &DXILOps, 308 raw_ostream &OS) { 309 // Sort by DXILOpID. 310 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) { 311 return A.DXILOpID < B.DXILOpID; 312 }); 313 314 // Collect Names. 315 SequenceToOffsetTable<std::string> OpClassStrings; 316 SequenceToOffsetTable<std::string> OpStrings; 317 SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters; 318 319 StringMap<SmallVector<ParameterKind>> ParameterMap; 320 StringSet<> ClassSet; 321 for (auto &DXILOp : DXILOps) { 322 OpStrings.add(DXILOp.DXILOp.str()); 323 324 if (ClassSet.find(DXILOp.DXILClass) != ClassSet.end()) 325 continue; 326 ClassSet.insert(DXILOp.DXILClass); 327 OpClassStrings.add(getDXILOpClassName(DXILOp.DXILClass)); 328 SmallVector<ParameterKind> ParamKindVec; 329 for (auto &Param : DXILOp.Params) { 330 ParamKindVec.emplace_back(Param.Kind); 331 } 332 ParameterMap[DXILOp.DXILClass] = ParamKindVec; 333 Parameters.add(ParamKindVec); 334 } 335 336 // Layout names. 337 OpStrings.layout(); 338 OpClassStrings.layout(); 339 Parameters.layout(); 340 341 // Emit the DXIL operation table. 342 //{DXIL::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary, 343 // OpCodeClassNameIndex, 344 // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0, 345 // 3, ParameterTableOffset}, 346 OS << "static const OpCodeProperty *getOpCodeProperty(DXIL::OpCode DXILOp) " 347 "{\n"; 348 349 OS << " static const OpCodeProperty OpCodeProps[] = {\n"; 350 for (auto &DXILOp : DXILOps) { 351 OS << " { DXIL::OpCode::" << DXILOp.DXILOp << ", " 352 << OpStrings.get(DXILOp.DXILOp.str()) 353 << ", OpCodeClass::" << DXILOp.DXILClass << ", " 354 << OpClassStrings.get(getDXILOpClassName(DXILOp.DXILClass)) << ", " 355 << getDXILOperationOverload(DXILOp.OverloadTypes) << ", " 356 << emitDXILOperationFnAttr(DXILOp.FnAttr) << ", " 357 << DXILOp.OverloadParamIndex << ", " << DXILOp.Params.size() << ", " 358 << Parameters.get(ParameterMap[DXILOp.DXILClass]) << " },\n"; 359 } 360 OS << " };\n"; 361 362 OS << " // FIXME: change search to indexing with\n"; 363 OS << " // DXILOp once all DXIL op is added.\n"; 364 OS << " OpCodeProperty TmpProp;\n"; 365 OS << " TmpProp.OpCode = DXILOp;\n"; 366 OS << " const OpCodeProperty *Prop =\n"; 367 OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n"; 368 OS << " [](const OpCodeProperty &A, const " 369 "OpCodeProperty &B) {\n"; 370 OS << " return A.OpCode < B.OpCode;\n"; 371 OS << " });\n"; 372 OS << " assert(Prop && \"fail to find OpCodeProperty\");\n"; 373 OS << " return Prop;\n"; 374 OS << "}\n\n"; 375 376 // Emit the string tables. 377 OS << "static const char *getOpCodeName(DXIL::OpCode DXILOp) {\n\n"; 378 379 OpStrings.emitStringLiteralDef(OS, 380 " static const char DXILOpCodeNameTable[]"); 381 382 OS << " auto *Prop = getOpCodeProperty(DXILOp);\n"; 383 OS << " unsigned Index = Prop->OpCodeNameOffset;\n"; 384 OS << " return DXILOpCodeNameTable + Index;\n"; 385 OS << "}\n\n"; 386 387 OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) " 388 "{\n\n"; 389 390 OpClassStrings.emitStringLiteralDef( 391 OS, " static const char DXILOpCodeClassNameTable[]"); 392 393 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n"; 394 OS << " return DXILOpCodeClassNameTable + Index;\n"; 395 OS << "}\n "; 396 397 OS << "static const ParameterKind *getOpCodeParameterKind(const " 398 "OpCodeProperty &Prop) " 399 "{\n\n"; 400 OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n"; 401 Parameters.emit( 402 OS, 403 [](raw_ostream &ParamOS, ParameterKind Kind) { 404 ParamOS << "ParameterKind::" << parameterKindToString(Kind); 405 }, 406 "ParameterKind::INVALID"); 407 OS << " };\n\n"; 408 OS << " unsigned Index = Prop.ParameterTableOffset;\n"; 409 OS << " return DXILOpParameterKindTable + Index;\n"; 410 OS << "}\n "; 411 } 412 413 namespace llvm { 414 415 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) { 416 std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op"); 417 OS << "// Generated code, do not edit.\n"; 418 OS << "\n"; 419 420 std::vector<DXILOperationData> DXILOps; 421 DXILOps.reserve(Ops.size()); 422 for (auto *Record : Ops) { 423 DXILOps.emplace_back(DXILOperationData(Record)); 424 } 425 426 OS << "#ifdef DXIL_OP_ENUM\n"; 427 emitDXILEnums(DXILOps, OS); 428 OS << "#endif\n\n"; 429 430 OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n"; 431 emitDXILIntrinsicMap(DXILOps, OS); 432 OS << "#endif\n\n"; 433 434 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n"; 435 emitDXILOperationTable(DXILOps, OS); 436 OS << "#endif\n\n"; 437 438 OS << "\n"; 439 } 440 441 } // namespace llvm 442