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 llvm_unreachable("Unknown llvm::DXIL::ParameterKind enum"); 148 } 149 150 static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) { 151 // Name = ID, // Doc 152 OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc 153 << "\n"; 154 } 155 156 static std::string buildCategoryStr(StringSet<> &Cetegorys) { 157 std::string Str; 158 raw_string_ostream OS(Str); 159 for (auto &It : Cetegorys) { 160 OS << " " << It.getKey(); 161 } 162 return OS.str(); 163 } 164 165 // Emit enum declaration for DXIL. 166 static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps, 167 raw_ostream &OS) { 168 // Sort by Category + OpName. 169 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) { 170 // Group by Category first. 171 if (A.Category == B.Category) 172 // Inside same Category, order by OpName. 173 return A.DXILOp < B.DXILOp; 174 else 175 return A.Category < B.Category; 176 }); 177 178 OS << "// Enumeration for operations specified by DXIL\n"; 179 OS << "enum class OpCode : unsigned {\n"; 180 181 StringMap<StringSet<>> ClassMap; 182 StringRef PrevCategory = ""; 183 for (auto &DXILOp : DXILOps) { 184 StringRef Category = DXILOp.Category; 185 if (Category != PrevCategory) { 186 OS << "\n// " << Category << "\n"; 187 PrevCategory = Category; 188 } 189 emitDXILOpEnum(DXILOp, OS); 190 auto It = ClassMap.find(DXILOp.DXILClass); 191 if (It != ClassMap.end()) { 192 It->second.insert(DXILOp.Category); 193 } else { 194 ClassMap[DXILOp.DXILClass].insert(DXILOp.Category); 195 } 196 } 197 198 OS << "\n};\n\n"; 199 200 std::vector<std::pair<std::string, std::string>> ClassVec; 201 for (auto &It : ClassMap) { 202 ClassVec.emplace_back( 203 std::make_pair(It.getKey().str(), buildCategoryStr(It.second))); 204 } 205 // Sort by Category + ClassName. 206 llvm::sort(ClassVec, [](std::pair<std::string, std::string> &A, 207 std::pair<std::string, std::string> &B) { 208 StringRef ClassA = A.first; 209 StringRef CategoryA = A.second; 210 StringRef ClassB = B.first; 211 StringRef CategoryB = B.second; 212 // Group by Category first. 213 if (CategoryA == CategoryB) 214 // Inside same Category, order by ClassName. 215 return ClassA < ClassB; 216 else 217 return CategoryA < CategoryB; 218 }); 219 220 OS << "// Groups for DXIL operations with equivalent function templates\n"; 221 OS << "enum class OpCodeClass : unsigned {\n"; 222 PrevCategory = ""; 223 for (auto &It : ClassVec) { 224 225 StringRef Category = It.second; 226 if (Category != PrevCategory) { 227 OS << "\n// " << Category << "\n"; 228 PrevCategory = Category; 229 } 230 StringRef Name = It.first; 231 OS << Name << ",\n"; 232 } 233 OS << "\n};\n\n"; 234 } 235 236 // Emit map from llvm intrinsic to DXIL operation. 237 static void emitDXILIntrinsicMap(std::vector<DXILOperationData> &DXILOps, 238 raw_ostream &OS) { 239 OS << "\n"; 240 // FIXME: use array instead of SmallDenseMap. 241 OS << "static const SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = " 242 "{\n"; 243 for (auto &DXILOp : DXILOps) { 244 if (DXILOp.Intrinsic.empty()) 245 continue; 246 // {Intrinsic::sin, DXIL::OpCode::Sin}, 247 OS << " { Intrinsic::" << DXILOp.Intrinsic 248 << ", DXIL::OpCode::" << DXILOp.DXILOp << "},\n"; 249 } 250 OS << "};\n"; 251 OS << "\n"; 252 } 253 254 static std::string emitDXILOperationFnAttr(StringRef FnAttr) { 255 return StringSwitch<std::string>(FnAttr) 256 .Case("rn", "Attribute::ReadNone") 257 .Case("ro", "Attribute::ReadOnly") 258 .Default("Attribute::None"); 259 } 260 261 static std::string getOverloadKind(StringRef Overload) { 262 return StringSwitch<std::string>(Overload) 263 .Case("half", "OverloadKind::HALF") 264 .Case("float", "OverloadKind::FLOAT") 265 .Case("double", "OverloadKind::DOUBLE") 266 .Case("i1", "OverloadKind::I1") 267 .Case("i16", "OverloadKind::I16") 268 .Case("i32", "OverloadKind::I32") 269 .Case("i64", "OverloadKind::I64") 270 .Case("udt", "OverloadKind::UserDefineType") 271 .Case("obj", "OverloadKind::ObjectType") 272 .Default("OverloadKind::VOID"); 273 } 274 275 static std::string getDXILOperationOverload(StringRef Overloads) { 276 SmallVector<StringRef> OverloadStrs; 277 Overloads.split(OverloadStrs, ';', /*MaxSplit*/ -1, /*KeepEmpty*/ false); 278 // Format is: OverloadKind::FLOAT | OverloadKind::HALF 279 assert(!OverloadStrs.empty() && "Invalid overloads"); 280 auto It = OverloadStrs.begin(); 281 std::string Result; 282 raw_string_ostream OS(Result); 283 OS << getOverloadKind(*It); 284 for (++It; It != OverloadStrs.end(); ++It) { 285 OS << " | " << getOverloadKind(*It); 286 } 287 return OS.str(); 288 } 289 290 static std::string lowerFirstLetter(StringRef Name) { 291 if (Name.empty()) 292 return ""; 293 294 std::string LowerName = Name.str(); 295 LowerName[0] = llvm::toLower(Name[0]); 296 return LowerName; 297 } 298 299 static std::string getDXILOpClassName(StringRef DXILOpClass) { 300 // Lower first letter expect for special case. 301 return StringSwitch<std::string>(DXILOpClass) 302 .Case("CBufferLoad", "cbufferLoad") 303 .Case("CBufferLoadLegacy", "cbufferLoadLegacy") 304 .Case("GSInstanceID", "gsInstanceID") 305 .Default(lowerFirstLetter(DXILOpClass)); 306 } 307 308 static void emitDXILOperationTable(std::vector<DXILOperationData> &DXILOps, 309 raw_ostream &OS) { 310 // Sort by DXILOpID. 311 llvm::sort(DXILOps, [](DXILOperationData &A, DXILOperationData &B) { 312 return A.DXILOpID < B.DXILOpID; 313 }); 314 315 // Collect Names. 316 SequenceToOffsetTable<std::string> OpClassStrings; 317 SequenceToOffsetTable<std::string> OpStrings; 318 SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters; 319 320 StringMap<SmallVector<ParameterKind>> ParameterMap; 321 StringSet<> ClassSet; 322 for (auto &DXILOp : DXILOps) { 323 OpStrings.add(DXILOp.DXILOp.str()); 324 325 if (ClassSet.find(DXILOp.DXILClass) != ClassSet.end()) 326 continue; 327 ClassSet.insert(DXILOp.DXILClass); 328 OpClassStrings.add(getDXILOpClassName(DXILOp.DXILClass)); 329 SmallVector<ParameterKind> ParamKindVec; 330 for (auto &Param : DXILOp.Params) { 331 ParamKindVec.emplace_back(Param.Kind); 332 } 333 ParameterMap[DXILOp.DXILClass] = ParamKindVec; 334 Parameters.add(ParamKindVec); 335 } 336 337 // Layout names. 338 OpStrings.layout(); 339 OpClassStrings.layout(); 340 Parameters.layout(); 341 342 // Emit the DXIL operation table. 343 //{DXIL::OpCode::Sin, OpCodeNameIndex, OpCodeClass::Unary, 344 // OpCodeClassNameIndex, 345 // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0, 346 // 3, ParameterTableOffset}, 347 OS << "static const OpCodeProperty *getOpCodeProperty(DXIL::OpCode DXILOp) " 348 "{\n"; 349 350 OS << " static const OpCodeProperty OpCodeProps[] = {\n"; 351 for (auto &DXILOp : DXILOps) { 352 OS << " { DXIL::OpCode::" << DXILOp.DXILOp << ", " 353 << OpStrings.get(DXILOp.DXILOp.str()) 354 << ", OpCodeClass::" << DXILOp.DXILClass << ", " 355 << OpClassStrings.get(getDXILOpClassName(DXILOp.DXILClass)) << ", " 356 << getDXILOperationOverload(DXILOp.OverloadTypes) << ", " 357 << emitDXILOperationFnAttr(DXILOp.FnAttr) << ", " 358 << DXILOp.OverloadParamIndex << ", " << DXILOp.Params.size() << ", " 359 << Parameters.get(ParameterMap[DXILOp.DXILClass]) << " },\n"; 360 } 361 OS << " };\n"; 362 363 OS << " // FIXME: change search to indexing with\n"; 364 OS << " // DXILOp once all DXIL op is added.\n"; 365 OS << " OpCodeProperty TmpProp;\n"; 366 OS << " TmpProp.OpCode = DXILOp;\n"; 367 OS << " const OpCodeProperty *Prop =\n"; 368 OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n"; 369 OS << " [](const OpCodeProperty &A, const " 370 "OpCodeProperty &B) {\n"; 371 OS << " return A.OpCode < B.OpCode;\n"; 372 OS << " });\n"; 373 OS << " assert(Prop && \"fail to find OpCodeProperty\");\n"; 374 OS << " return Prop;\n"; 375 OS << "}\n\n"; 376 377 // Emit the string tables. 378 OS << "static const char *getOpCodeName(DXIL::OpCode DXILOp) {\n\n"; 379 380 OpStrings.emitStringLiteralDef(OS, 381 " static const char DXILOpCodeNameTable[]"); 382 383 OS << " auto *Prop = getOpCodeProperty(DXILOp);\n"; 384 OS << " unsigned Index = Prop->OpCodeNameOffset;\n"; 385 OS << " return DXILOpCodeNameTable + Index;\n"; 386 OS << "}\n\n"; 387 388 OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) " 389 "{\n\n"; 390 391 OpClassStrings.emitStringLiteralDef( 392 OS, " static const char DXILOpCodeClassNameTable[]"); 393 394 OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n"; 395 OS << " return DXILOpCodeClassNameTable + Index;\n"; 396 OS << "}\n "; 397 398 OS << "static const ParameterKind *getOpCodeParameterKind(const " 399 "OpCodeProperty &Prop) " 400 "{\n\n"; 401 OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n"; 402 Parameters.emit( 403 OS, 404 [](raw_ostream &ParamOS, ParameterKind Kind) { 405 ParamOS << "ParameterKind::" << parameterKindToString(Kind); 406 }, 407 "ParameterKind::INVALID"); 408 OS << " };\n\n"; 409 OS << " unsigned Index = Prop.ParameterTableOffset;\n"; 410 OS << " return DXILOpParameterKindTable + Index;\n"; 411 OS << "}\n "; 412 } 413 414 namespace llvm { 415 416 void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) { 417 std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op"); 418 OS << "// Generated code, do not edit.\n"; 419 OS << "\n"; 420 421 std::vector<DXILOperationData> DXILOps; 422 DXILOps.reserve(Ops.size()); 423 for (auto *Record : Ops) { 424 DXILOps.emplace_back(DXILOperationData(Record)); 425 } 426 427 OS << "#ifdef DXIL_OP_ENUM\n"; 428 emitDXILEnums(DXILOps, OS); 429 OS << "#endif\n\n"; 430 431 OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n"; 432 emitDXILIntrinsicMap(DXILOps, OS); 433 OS << "#endif\n\n"; 434 435 OS << "#ifdef DXIL_OP_OPERATION_TABLE\n"; 436 emitDXILOperationTable(DXILOps, OS); 437 OS << "#endif\n\n"; 438 439 OS << "\n"; 440 } 441 442 } // namespace llvm 443