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