DXILEmitter.cpp 15 KB

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