181ad6265SDimitry Andric //===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // DXILEmitter uses the descriptions of DXIL operation to construct enum and
1081ad6265SDimitry Andric // helper functions for DXIL operation.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric
14*0fca6ea1SDimitry Andric #include "Basic/SequenceToOffsetTable.h"
15*0fca6ea1SDimitry Andric #include "Common/CodeGenTarget.h"
1681ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
17*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallSet.h"
1881ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h"
1981ad6265SDimitry Andric #include "llvm/ADT/StringSet.h"
2081ad6265SDimitry Andric #include "llvm/ADT/StringSwitch.h"
21*0fca6ea1SDimitry Andric #include "llvm/CodeGenTypes/MachineValueType.h"
22*0fca6ea1SDimitry Andric #include "llvm/Support/DXILABI.h"
2381ad6265SDimitry Andric #include "llvm/TableGen/Record.h"
2406c3fb27SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
25*0fca6ea1SDimitry Andric #include <string>
2681ad6265SDimitry Andric
2781ad6265SDimitry Andric using namespace llvm;
28bdd1243dSDimitry Andric using namespace llvm::dxil;
2981ad6265SDimitry Andric
3081ad6265SDimitry Andric namespace {
3181ad6265SDimitry Andric
3281ad6265SDimitry Andric struct DXILShaderModel {
3306c3fb27SDimitry Andric int Major = 0;
3406c3fb27SDimitry Andric int Minor = 0;
3581ad6265SDimitry Andric };
36972a253aSDimitry Andric
37*0fca6ea1SDimitry Andric struct DXILOperationDesc {
38*0fca6ea1SDimitry Andric std::string OpName; // name of DXIL operation
39*0fca6ea1SDimitry Andric int OpCode; // ID of DXIL operation
40*0fca6ea1SDimitry Andric StringRef OpClass; // name of the opcode class
4181ad6265SDimitry Andric StringRef Doc; // the documentation description of this instruction
42*0fca6ea1SDimitry Andric SmallVector<Record *> OpTypes; // Vector of operand type records -
43*0fca6ea1SDimitry Andric // return type is at index 0
44*0fca6ea1SDimitry Andric SmallVector<std::string>
45*0fca6ea1SDimitry Andric OpAttributes; // operation attribute represented as strings
46*0fca6ea1SDimitry Andric StringRef Intrinsic; // The llvm intrinsic map to OpName. Default is "" which
47*0fca6ea1SDimitry Andric // means no map exists
4806c3fb27SDimitry Andric bool IsDeriv = false; // whether this is some kind of derivative
4906c3fb27SDimitry Andric bool IsGradient = false; // whether this requires a gradient calculation
5006c3fb27SDimitry Andric bool IsFeedback = false; // whether this is a sampler feedback op
51*0fca6ea1SDimitry Andric bool IsWave =
52*0fca6ea1SDimitry Andric false; // whether this requires in-wave, cross-lane functionality
5306c3fb27SDimitry Andric bool RequiresUniformInputs = false; // whether this operation requires that
5406c3fb27SDimitry Andric // all of its inputs are uniform across
5506c3fb27SDimitry Andric // the wave
5681ad6265SDimitry Andric SmallVector<StringRef, 4>
5781ad6265SDimitry Andric ShaderStages; // shader stages to which this applies, empty for all.
5881ad6265SDimitry Andric DXILShaderModel ShaderModel; // minimum shader model required
5981ad6265SDimitry Andric DXILShaderModel ShaderModelTranslated; // minimum shader model required with
6081ad6265SDimitry Andric // translation by linker
61*0fca6ea1SDimitry Andric int OverloadParamIndex; // Index of parameter with overload type.
62*0fca6ea1SDimitry Andric // -1 : no overload types
6381ad6265SDimitry Andric SmallVector<StringRef, 4> counters; // counters for this inst.
64*0fca6ea1SDimitry Andric DXILOperationDesc(const Record *);
65*0fca6ea1SDimitry Andric };
66*0fca6ea1SDimitry Andric } // end anonymous namespace
6781ad6265SDimitry Andric
68*0fca6ea1SDimitry Andric /// Return dxil::ParameterKind corresponding to input LLVMType record
69*0fca6ea1SDimitry Andric ///
70*0fca6ea1SDimitry Andric /// \param R TableGen def record of class LLVMType
71*0fca6ea1SDimitry Andric /// \return ParameterKind As defined in llvm/Support/DXILABI.h
72*0fca6ea1SDimitry Andric
getParameterKind(const Record * R)73*0fca6ea1SDimitry Andric static ParameterKind getParameterKind(const Record *R) {
74*0fca6ea1SDimitry Andric auto VTRec = R->getValueAsDef("VT");
75*0fca6ea1SDimitry Andric switch (getValueType(VTRec)) {
76*0fca6ea1SDimitry Andric case MVT::isVoid:
77*0fca6ea1SDimitry Andric return ParameterKind::Void;
78*0fca6ea1SDimitry Andric case MVT::f16:
79*0fca6ea1SDimitry Andric return ParameterKind::Half;
80*0fca6ea1SDimitry Andric case MVT::f32:
81*0fca6ea1SDimitry Andric return ParameterKind::Float;
82*0fca6ea1SDimitry Andric case MVT::f64:
83*0fca6ea1SDimitry Andric return ParameterKind::Double;
84*0fca6ea1SDimitry Andric case MVT::i1:
85*0fca6ea1SDimitry Andric return ParameterKind::I1;
86*0fca6ea1SDimitry Andric case MVT::i8:
87*0fca6ea1SDimitry Andric return ParameterKind::I8;
88*0fca6ea1SDimitry Andric case MVT::i16:
89*0fca6ea1SDimitry Andric return ParameterKind::I16;
90*0fca6ea1SDimitry Andric case MVT::i32:
91*0fca6ea1SDimitry Andric return ParameterKind::I32;
92*0fca6ea1SDimitry Andric case MVT::fAny:
93*0fca6ea1SDimitry Andric case MVT::iAny:
94*0fca6ea1SDimitry Andric return ParameterKind::Overload;
95*0fca6ea1SDimitry Andric case MVT::Other:
96*0fca6ea1SDimitry Andric // Handle DXIL-specific overload types
97*0fca6ea1SDimitry Andric if (R->getValueAsInt("isHalfOrFloat") || R->getValueAsInt("isI16OrI32")) {
98*0fca6ea1SDimitry Andric return ParameterKind::Overload;
99*0fca6ea1SDimitry Andric }
100*0fca6ea1SDimitry Andric [[fallthrough]];
101*0fca6ea1SDimitry Andric default:
102*0fca6ea1SDimitry Andric llvm_unreachable("Support for specified DXIL Type not yet implemented");
103*0fca6ea1SDimitry Andric }
104*0fca6ea1SDimitry Andric }
105*0fca6ea1SDimitry Andric
106*0fca6ea1SDimitry Andric /// Construct an object using the DXIL Operation records specified
107*0fca6ea1SDimitry Andric /// in DXIL.td. This serves as the single source of reference of
108*0fca6ea1SDimitry Andric /// the information extracted from the specified Record R, for
109*0fca6ea1SDimitry Andric /// C++ code generated by this TableGen backend.
110*0fca6ea1SDimitry Andric // \param R Object representing TableGen record of a DXIL Operation
DXILOperationDesc(const Record * R)111*0fca6ea1SDimitry Andric DXILOperationDesc::DXILOperationDesc(const Record *R) {
112*0fca6ea1SDimitry Andric OpName = R->getNameInitAsString();
113*0fca6ea1SDimitry Andric OpCode = R->getValueAsInt("OpCode");
114*0fca6ea1SDimitry Andric
115*0fca6ea1SDimitry Andric Doc = R->getValueAsString("Doc");
116*0fca6ea1SDimitry Andric
117*0fca6ea1SDimitry Andric auto TypeRecs = R->getValueAsListOfDefs("OpTypes");
118*0fca6ea1SDimitry Andric unsigned TypeRecsSize = TypeRecs.size();
119*0fca6ea1SDimitry Andric // Populate OpTypes with return type and parameter types
120*0fca6ea1SDimitry Andric
121*0fca6ea1SDimitry Andric // Parameter indices of overloaded parameters.
122*0fca6ea1SDimitry Andric // This vector contains overload parameters in the order used to
123*0fca6ea1SDimitry Andric // resolve an LLVMMatchType in accordance with convention outlined in
124*0fca6ea1SDimitry Andric // the comment before the definition of class LLVMMatchType in
125*0fca6ea1SDimitry Andric // llvm/IR/Intrinsics.td
126*0fca6ea1SDimitry Andric SmallVector<int> OverloadParamIndices;
127*0fca6ea1SDimitry Andric for (unsigned i = 0; i < TypeRecsSize; i++) {
128*0fca6ea1SDimitry Andric auto TR = TypeRecs[i];
129*0fca6ea1SDimitry Andric // Track operation parameter indices of any overload types
130*0fca6ea1SDimitry Andric auto isAny = TR->getValueAsInt("isAny");
131*0fca6ea1SDimitry Andric if (isAny == 1) {
132*0fca6ea1SDimitry Andric // TODO: At present it is expected that all overload types in a DXIL Op
133*0fca6ea1SDimitry Andric // are of the same type. Hence, OverloadParamIndices will have only one
134*0fca6ea1SDimitry Andric // element. This implies we do not need a vector. However, until more
135*0fca6ea1SDimitry Andric // (all?) DXIL Ops are added in DXIL.td, a vector is being used to flag
136*0fca6ea1SDimitry Andric // cases this assumption would not hold.
137*0fca6ea1SDimitry Andric if (!OverloadParamIndices.empty()) {
138*0fca6ea1SDimitry Andric bool knownType = true;
139*0fca6ea1SDimitry Andric // Ensure that the same overload type registered earlier is being used
140*0fca6ea1SDimitry Andric for (auto Idx : OverloadParamIndices) {
141*0fca6ea1SDimitry Andric if (TR != TypeRecs[Idx]) {
142*0fca6ea1SDimitry Andric knownType = false;
143*0fca6ea1SDimitry Andric break;
144*0fca6ea1SDimitry Andric }
145*0fca6ea1SDimitry Andric }
146*0fca6ea1SDimitry Andric if (!knownType) {
147*0fca6ea1SDimitry Andric report_fatal_error("Specification of multiple differing overload "
148*0fca6ea1SDimitry Andric "parameter types not yet supported",
149*0fca6ea1SDimitry Andric false);
150*0fca6ea1SDimitry Andric }
151*0fca6ea1SDimitry Andric } else {
152*0fca6ea1SDimitry Andric OverloadParamIndices.push_back(i);
153*0fca6ea1SDimitry Andric }
154*0fca6ea1SDimitry Andric }
155*0fca6ea1SDimitry Andric // Populate OpTypes array according to the type specification
156*0fca6ea1SDimitry Andric if (TR->isAnonymous()) {
157*0fca6ea1SDimitry Andric // Check prior overload types exist
158*0fca6ea1SDimitry Andric assert(!OverloadParamIndices.empty() &&
159*0fca6ea1SDimitry Andric "No prior overloaded parameter found to match.");
160*0fca6ea1SDimitry Andric // Get the parameter index of anonymous type, TR, references
161*0fca6ea1SDimitry Andric auto OLParamIndex = TR->getValueAsInt("Number");
162*0fca6ea1SDimitry Andric // Resolve and insert the type to that at OLParamIndex
163*0fca6ea1SDimitry Andric OpTypes.emplace_back(TypeRecs[OLParamIndex]);
164*0fca6ea1SDimitry Andric } else {
165*0fca6ea1SDimitry Andric // A non-anonymous type. Just record it in OpTypes
166*0fca6ea1SDimitry Andric OpTypes.emplace_back(TR);
167*0fca6ea1SDimitry Andric }
168*0fca6ea1SDimitry Andric }
169*0fca6ea1SDimitry Andric
170*0fca6ea1SDimitry Andric // Set the index of the overload parameter, if any.
171*0fca6ea1SDimitry Andric OverloadParamIndex = -1; // default; indicating none
172*0fca6ea1SDimitry Andric if (!OverloadParamIndices.empty()) {
173*0fca6ea1SDimitry Andric if (OverloadParamIndices.size() > 1)
174*0fca6ea1SDimitry Andric report_fatal_error("Multiple overload type specification not supported",
175*0fca6ea1SDimitry Andric false);
176*0fca6ea1SDimitry Andric OverloadParamIndex = OverloadParamIndices[0];
177*0fca6ea1SDimitry Andric }
178*0fca6ea1SDimitry Andric // Get the operation class
179*0fca6ea1SDimitry Andric OpClass = R->getValueAsDef("OpClass")->getName();
180*0fca6ea1SDimitry Andric
181*0fca6ea1SDimitry Andric if (R->getValue("LLVMIntrinsic")) {
182*0fca6ea1SDimitry Andric auto *IntrinsicDef = R->getValueAsDef("LLVMIntrinsic");
18381ad6265SDimitry Andric auto DefName = IntrinsicDef->getName();
1845f757f3fSDimitry Andric assert(DefName.starts_with("int_") && "invalid intrinsic name");
18581ad6265SDimitry Andric // Remove the int_ from intrinsic name.
18681ad6265SDimitry Andric Intrinsic = DefName.substr(4);
187*0fca6ea1SDimitry Andric // TODO: For now, assume that attributes of DXIL Operation are the same as
188*0fca6ea1SDimitry Andric // that of the intrinsic. Deviations are expected to be encoded in TableGen
189*0fca6ea1SDimitry Andric // record specification and handled accordingly here. Support to be added
190*0fca6ea1SDimitry Andric // as needed.
191*0fca6ea1SDimitry Andric auto IntrPropList = IntrinsicDef->getValueAsListInit("IntrProperties");
192*0fca6ea1SDimitry Andric auto IntrPropListSize = IntrPropList->size();
193*0fca6ea1SDimitry Andric for (unsigned i = 0; i < IntrPropListSize; i++) {
194*0fca6ea1SDimitry Andric OpAttributes.emplace_back(IntrPropList->getElement(i)->getAsString());
195*0fca6ea1SDimitry Andric }
196*0fca6ea1SDimitry Andric }
19781ad6265SDimitry Andric }
19881ad6265SDimitry Andric
199*0fca6ea1SDimitry Andric /// Return a string representation of ParameterKind enum
200*0fca6ea1SDimitry Andric /// \param Kind Parameter Kind enum value
201*0fca6ea1SDimitry Andric /// \return std::string string representation of input Kind
getParameterKindStr(ParameterKind Kind)202*0fca6ea1SDimitry Andric static std::string getParameterKindStr(ParameterKind Kind) {
203972a253aSDimitry Andric switch (Kind) {
204*0fca6ea1SDimitry Andric case ParameterKind::Invalid:
205*0fca6ea1SDimitry Andric return "Invalid";
206*0fca6ea1SDimitry Andric case ParameterKind::Void:
207*0fca6ea1SDimitry Andric return "Void";
208*0fca6ea1SDimitry Andric case ParameterKind::Half:
209*0fca6ea1SDimitry Andric return "Half";
210*0fca6ea1SDimitry Andric case ParameterKind::Float:
211*0fca6ea1SDimitry Andric return "Float";
212*0fca6ea1SDimitry Andric case ParameterKind::Double:
213*0fca6ea1SDimitry Andric return "Double";
214972a253aSDimitry Andric case ParameterKind::I1:
215972a253aSDimitry Andric return "I1";
216972a253aSDimitry Andric case ParameterKind::I8:
217972a253aSDimitry Andric return "I8";
218972a253aSDimitry Andric case ParameterKind::I16:
219972a253aSDimitry Andric return "I16";
220972a253aSDimitry Andric case ParameterKind::I32:
221972a253aSDimitry Andric return "I32";
222972a253aSDimitry Andric case ParameterKind::I64:
223972a253aSDimitry Andric return "I64";
224*0fca6ea1SDimitry Andric case ParameterKind::Overload:
225*0fca6ea1SDimitry Andric return "Overload";
226*0fca6ea1SDimitry Andric case ParameterKind::CBufferRet:
227*0fca6ea1SDimitry Andric return "CBufferRet";
228*0fca6ea1SDimitry Andric case ParameterKind::ResourceRet:
229*0fca6ea1SDimitry Andric return "ResourceRet";
230*0fca6ea1SDimitry Andric case ParameterKind::DXILHandle:
231*0fca6ea1SDimitry Andric return "DXILHandle";
232972a253aSDimitry Andric }
233bdd1243dSDimitry Andric llvm_unreachable("Unknown llvm::dxil::ParameterKind enum");
234972a253aSDimitry Andric }
235972a253aSDimitry Andric
236*0fca6ea1SDimitry Andric /// Return a string representation of OverloadKind enum that maps to
237*0fca6ea1SDimitry Andric /// input LLVMType record
238*0fca6ea1SDimitry Andric /// \param R TableGen def record of class LLVMType
239*0fca6ea1SDimitry Andric /// \return std::string string representation of OverloadKind
240*0fca6ea1SDimitry Andric
getOverloadKindStr(const Record * R)241*0fca6ea1SDimitry Andric static std::string getOverloadKindStr(const Record *R) {
242*0fca6ea1SDimitry Andric auto VTRec = R->getValueAsDef("VT");
243*0fca6ea1SDimitry Andric switch (getValueType(VTRec)) {
244*0fca6ea1SDimitry Andric case MVT::isVoid:
245*0fca6ea1SDimitry Andric return "OverloadKind::VOID";
246*0fca6ea1SDimitry Andric case MVT::f16:
247*0fca6ea1SDimitry Andric return "OverloadKind::HALF";
248*0fca6ea1SDimitry Andric case MVT::f32:
249*0fca6ea1SDimitry Andric return "OverloadKind::FLOAT";
250*0fca6ea1SDimitry Andric case MVT::f64:
251*0fca6ea1SDimitry Andric return "OverloadKind::DOUBLE";
252*0fca6ea1SDimitry Andric case MVT::i1:
253*0fca6ea1SDimitry Andric return "OverloadKind::I1";
254*0fca6ea1SDimitry Andric case MVT::i8:
255*0fca6ea1SDimitry Andric return "OverloadKind::I8";
256*0fca6ea1SDimitry Andric case MVT::i16:
257*0fca6ea1SDimitry Andric return "OverloadKind::I16";
258*0fca6ea1SDimitry Andric case MVT::i32:
259*0fca6ea1SDimitry Andric return "OverloadKind::I32";
260*0fca6ea1SDimitry Andric case MVT::i64:
261*0fca6ea1SDimitry Andric return "OverloadKind::I64";
262*0fca6ea1SDimitry Andric case MVT::iAny:
263*0fca6ea1SDimitry Andric return "OverloadKind::I16 | OverloadKind::I32 | OverloadKind::I64";
264*0fca6ea1SDimitry Andric case MVT::fAny:
265*0fca6ea1SDimitry Andric return "OverloadKind::HALF | OverloadKind::FLOAT | OverloadKind::DOUBLE";
266*0fca6ea1SDimitry Andric case MVT::Other:
267*0fca6ea1SDimitry Andric // Handle DXIL-specific overload types
268*0fca6ea1SDimitry Andric {
269*0fca6ea1SDimitry Andric if (R->getValueAsInt("isHalfOrFloat")) {
270*0fca6ea1SDimitry Andric return "OverloadKind::HALF | OverloadKind::FLOAT";
271*0fca6ea1SDimitry Andric } else if (R->getValueAsInt("isI16OrI32")) {
272*0fca6ea1SDimitry Andric return "OverloadKind::I16 | OverloadKind::I32";
273*0fca6ea1SDimitry Andric }
274*0fca6ea1SDimitry Andric }
275*0fca6ea1SDimitry Andric [[fallthrough]];
276*0fca6ea1SDimitry Andric default:
277*0fca6ea1SDimitry Andric llvm_unreachable(
278*0fca6ea1SDimitry Andric "Support for specified parameter OverloadKind not yet implemented");
279*0fca6ea1SDimitry Andric }
28081ad6265SDimitry Andric }
28181ad6265SDimitry Andric
282*0fca6ea1SDimitry Andric /// Emit Enums of DXIL Ops
283*0fca6ea1SDimitry Andric /// \param A vector of DXIL Ops
284*0fca6ea1SDimitry Andric /// \param Output stream
emitDXILEnums(std::vector<DXILOperationDesc> & Ops,raw_ostream & OS)285*0fca6ea1SDimitry Andric static void emitDXILEnums(std::vector<DXILOperationDesc> &Ops,
28681ad6265SDimitry Andric raw_ostream &OS) {
287*0fca6ea1SDimitry Andric // Sort by OpCode
288*0fca6ea1SDimitry Andric llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {
289*0fca6ea1SDimitry Andric return A.OpCode < B.OpCode;
29081ad6265SDimitry Andric });
29181ad6265SDimitry Andric
29281ad6265SDimitry Andric OS << "// Enumeration for operations specified by DXIL\n";
29381ad6265SDimitry Andric OS << "enum class OpCode : unsigned {\n";
29481ad6265SDimitry Andric
295*0fca6ea1SDimitry Andric for (auto &Op : Ops) {
296*0fca6ea1SDimitry Andric // Name = ID, // Doc
297*0fca6ea1SDimitry Andric OS << Op.OpName << " = " << Op.OpCode << ", // " << Op.Doc << "\n";
29881ad6265SDimitry Andric }
29981ad6265SDimitry Andric
30081ad6265SDimitry Andric OS << "\n};\n\n";
30181ad6265SDimitry Andric
30281ad6265SDimitry Andric OS << "// Groups for DXIL operations with equivalent function templates\n";
30381ad6265SDimitry Andric OS << "enum class OpCodeClass : unsigned {\n";
304*0fca6ea1SDimitry Andric // Build an OpClass set to print
305*0fca6ea1SDimitry Andric SmallSet<StringRef, 2> OpClassSet;
306*0fca6ea1SDimitry Andric for (auto &Op : Ops) {
307*0fca6ea1SDimitry Andric OpClassSet.insert(Op.OpClass);
30881ad6265SDimitry Andric }
309*0fca6ea1SDimitry Andric for (auto &C : OpClassSet) {
310*0fca6ea1SDimitry Andric OS << C << ",\n";
31181ad6265SDimitry Andric }
31281ad6265SDimitry Andric OS << "\n};\n\n";
31381ad6265SDimitry Andric }
31481ad6265SDimitry Andric
315*0fca6ea1SDimitry Andric /// Emit map of DXIL operation to LLVM or DirectX intrinsic
316*0fca6ea1SDimitry Andric /// \param A vector of DXIL Ops
317*0fca6ea1SDimitry Andric /// \param Output stream
emitDXILIntrinsicMap(std::vector<DXILOperationDesc> & Ops,raw_ostream & OS)318*0fca6ea1SDimitry Andric static void emitDXILIntrinsicMap(std::vector<DXILOperationDesc> &Ops,
31981ad6265SDimitry Andric raw_ostream &OS) {
32081ad6265SDimitry Andric OS << "\n";
32181ad6265SDimitry Andric // FIXME: use array instead of SmallDenseMap.
322bdd1243dSDimitry Andric OS << "static const SmallDenseMap<Intrinsic::ID, dxil::OpCode> LowerMap = "
32381ad6265SDimitry Andric "{\n";
324*0fca6ea1SDimitry Andric for (auto &Op : Ops) {
325*0fca6ea1SDimitry Andric if (Op.Intrinsic.empty())
32681ad6265SDimitry Andric continue;
327bdd1243dSDimitry Andric // {Intrinsic::sin, dxil::OpCode::Sin},
328*0fca6ea1SDimitry Andric OS << " { Intrinsic::" << Op.Intrinsic << ", dxil::OpCode::" << Op.OpName
329*0fca6ea1SDimitry Andric << "},\n";
33081ad6265SDimitry Andric }
33181ad6265SDimitry Andric OS << "};\n";
33281ad6265SDimitry Andric OS << "\n";
33381ad6265SDimitry Andric }
33481ad6265SDimitry Andric
335*0fca6ea1SDimitry Andric /// Convert operation attribute string to Attribute enum
336*0fca6ea1SDimitry Andric ///
337*0fca6ea1SDimitry Andric /// \param Attr string reference
338*0fca6ea1SDimitry Andric /// \return std::string Attribute enum string
339*0fca6ea1SDimitry Andric
emitDXILOperationAttr(SmallVector<std::string> Attrs)340*0fca6ea1SDimitry Andric static std::string emitDXILOperationAttr(SmallVector<std::string> Attrs) {
341*0fca6ea1SDimitry Andric for (auto Attr : Attrs) {
342*0fca6ea1SDimitry Andric // TODO: For now just recognize IntrNoMem and IntrReadMem as valid and
343*0fca6ea1SDimitry Andric // ignore others.
344*0fca6ea1SDimitry Andric if (Attr == "IntrNoMem") {
345*0fca6ea1SDimitry Andric return "Attribute::ReadNone";
346*0fca6ea1SDimitry Andric } else if (Attr == "IntrReadMem") {
347*0fca6ea1SDimitry Andric return "Attribute::ReadOnly";
348*0fca6ea1SDimitry Andric }
349*0fca6ea1SDimitry Andric }
350*0fca6ea1SDimitry Andric return "Attribute::None";
35181ad6265SDimitry Andric }
35281ad6265SDimitry Andric
353*0fca6ea1SDimitry Andric /// Emit DXIL operation table
354*0fca6ea1SDimitry Andric /// \param A vector of DXIL Ops
355*0fca6ea1SDimitry Andric /// \param Output stream
emitDXILOperationTable(std::vector<DXILOperationDesc> & Ops,raw_ostream & OS)356*0fca6ea1SDimitry Andric static void emitDXILOperationTable(std::vector<DXILOperationDesc> &Ops,
35781ad6265SDimitry Andric raw_ostream &OS) {
358*0fca6ea1SDimitry Andric // Sort by OpCode.
359*0fca6ea1SDimitry Andric llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {
360*0fca6ea1SDimitry Andric return A.OpCode < B.OpCode;
36181ad6265SDimitry Andric });
36281ad6265SDimitry Andric
36381ad6265SDimitry Andric // Collect Names.
36481ad6265SDimitry Andric SequenceToOffsetTable<std::string> OpClassStrings;
36581ad6265SDimitry Andric SequenceToOffsetTable<std::string> OpStrings;
366972a253aSDimitry Andric SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters;
36781ad6265SDimitry Andric
368972a253aSDimitry Andric StringMap<SmallVector<ParameterKind>> ParameterMap;
36981ad6265SDimitry Andric StringSet<> ClassSet;
370*0fca6ea1SDimitry Andric for (auto &Op : Ops) {
371*0fca6ea1SDimitry Andric OpStrings.add(Op.OpName);
37281ad6265SDimitry Andric
373*0fca6ea1SDimitry Andric if (ClassSet.contains(Op.OpClass))
37481ad6265SDimitry Andric continue;
375*0fca6ea1SDimitry Andric ClassSet.insert(Op.OpClass);
376*0fca6ea1SDimitry Andric OpClassStrings.add(Op.OpClass.data());
377972a253aSDimitry Andric SmallVector<ParameterKind> ParamKindVec;
378*0fca6ea1SDimitry Andric // ParamKindVec is a vector of parameters. Skip return type at index 0
379*0fca6ea1SDimitry Andric for (unsigned i = 1; i < Op.OpTypes.size(); i++) {
380*0fca6ea1SDimitry Andric ParamKindVec.emplace_back(getParameterKind(Op.OpTypes[i]));
381972a253aSDimitry Andric }
382*0fca6ea1SDimitry Andric ParameterMap[Op.OpClass] = ParamKindVec;
383972a253aSDimitry Andric Parameters.add(ParamKindVec);
38481ad6265SDimitry Andric }
38581ad6265SDimitry Andric
38681ad6265SDimitry Andric // Layout names.
38781ad6265SDimitry Andric OpStrings.layout();
38881ad6265SDimitry Andric OpClassStrings.layout();
389972a253aSDimitry Andric Parameters.layout();
39081ad6265SDimitry Andric
39181ad6265SDimitry Andric // Emit the DXIL operation table.
392*0fca6ea1SDimitry Andric //{dxil::OpCode::Sin, OpCodeNameIndex, OpCodeClass::unary,
39381ad6265SDimitry Andric // OpCodeClassNameIndex,
394972a253aSDimitry Andric // OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0,
395972a253aSDimitry Andric // 3, ParameterTableOffset},
396*0fca6ea1SDimitry Andric OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) "
39781ad6265SDimitry Andric "{\n";
39881ad6265SDimitry Andric
39981ad6265SDimitry Andric OS << " static const OpCodeProperty OpCodeProps[] = {\n";
400*0fca6ea1SDimitry Andric for (auto &Op : Ops) {
401*0fca6ea1SDimitry Andric // Consider Op.OverloadParamIndex as the overload parameter index, by
402*0fca6ea1SDimitry Andric // default
403*0fca6ea1SDimitry Andric auto OLParamIdx = Op.OverloadParamIndex;
404*0fca6ea1SDimitry Andric // If no overload parameter index is set, treat first parameter type as
405*0fca6ea1SDimitry Andric // overload type - unless the Op has no parameters, in which case treat the
406*0fca6ea1SDimitry Andric // return type - as overload parameter to emit the appropriate overload kind
407*0fca6ea1SDimitry Andric // enum.
408*0fca6ea1SDimitry Andric if (OLParamIdx < 0) {
409*0fca6ea1SDimitry Andric OLParamIdx = (Op.OpTypes.size() > 1) ? 1 : 0;
410*0fca6ea1SDimitry Andric }
411*0fca6ea1SDimitry Andric OS << " { dxil::OpCode::" << Op.OpName << ", " << OpStrings.get(Op.OpName)
412*0fca6ea1SDimitry Andric << ", OpCodeClass::" << Op.OpClass << ", "
413*0fca6ea1SDimitry Andric << OpClassStrings.get(Op.OpClass.data()) << ", "
414*0fca6ea1SDimitry Andric << getOverloadKindStr(Op.OpTypes[OLParamIdx]) << ", "
415*0fca6ea1SDimitry Andric << emitDXILOperationAttr(Op.OpAttributes) << ", "
416*0fca6ea1SDimitry Andric << Op.OverloadParamIndex << ", " << Op.OpTypes.size() - 1 << ", "
417*0fca6ea1SDimitry Andric << Parameters.get(ParameterMap[Op.OpClass]) << " },\n";
41881ad6265SDimitry Andric }
41981ad6265SDimitry Andric OS << " };\n";
42081ad6265SDimitry Andric
42181ad6265SDimitry Andric OS << " // FIXME: change search to indexing with\n";
422*0fca6ea1SDimitry Andric OS << " // Op once all DXIL operations are added.\n";
42381ad6265SDimitry Andric OS << " OpCodeProperty TmpProp;\n";
424*0fca6ea1SDimitry Andric OS << " TmpProp.OpCode = Op;\n";
42581ad6265SDimitry Andric OS << " const OpCodeProperty *Prop =\n";
42681ad6265SDimitry Andric OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n";
42781ad6265SDimitry Andric OS << " [](const OpCodeProperty &A, const "
42881ad6265SDimitry Andric "OpCodeProperty &B) {\n";
42981ad6265SDimitry Andric OS << " return A.OpCode < B.OpCode;\n";
43081ad6265SDimitry Andric OS << " });\n";
431*0fca6ea1SDimitry Andric OS << " assert(Prop && \"failed to find OpCodeProperty\");\n";
43281ad6265SDimitry Andric OS << " return Prop;\n";
43381ad6265SDimitry Andric OS << "}\n\n";
43481ad6265SDimitry Andric
43581ad6265SDimitry Andric // Emit the string tables.
436*0fca6ea1SDimitry Andric OS << "static const char *getOpCodeName(dxil::OpCode Op) {\n\n";
43781ad6265SDimitry Andric
43881ad6265SDimitry Andric OpStrings.emitStringLiteralDef(OS,
43981ad6265SDimitry Andric " static const char DXILOpCodeNameTable[]");
44081ad6265SDimitry Andric
441*0fca6ea1SDimitry Andric OS << " auto *Prop = getOpCodeProperty(Op);\n";
44281ad6265SDimitry Andric OS << " unsigned Index = Prop->OpCodeNameOffset;\n";
44381ad6265SDimitry Andric OS << " return DXILOpCodeNameTable + Index;\n";
44481ad6265SDimitry Andric OS << "}\n\n";
44581ad6265SDimitry Andric
44681ad6265SDimitry Andric OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
44781ad6265SDimitry Andric "{\n\n";
44881ad6265SDimitry Andric
44981ad6265SDimitry Andric OpClassStrings.emitStringLiteralDef(
45081ad6265SDimitry Andric OS, " static const char DXILOpCodeClassNameTable[]");
45181ad6265SDimitry Andric
45281ad6265SDimitry Andric OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";
45381ad6265SDimitry Andric OS << " return DXILOpCodeClassNameTable + Index;\n";
45481ad6265SDimitry Andric OS << "}\n ";
455972a253aSDimitry Andric
456972a253aSDimitry Andric OS << "static const ParameterKind *getOpCodeParameterKind(const "
457972a253aSDimitry Andric "OpCodeProperty &Prop) "
458972a253aSDimitry Andric "{\n\n";
459972a253aSDimitry Andric OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n";
460972a253aSDimitry Andric Parameters.emit(
461972a253aSDimitry Andric OS,
462972a253aSDimitry Andric [](raw_ostream &ParamOS, ParameterKind Kind) {
463*0fca6ea1SDimitry Andric ParamOS << "ParameterKind::" << getParameterKindStr(Kind);
464972a253aSDimitry Andric },
465*0fca6ea1SDimitry Andric "ParameterKind::Invalid");
466972a253aSDimitry Andric OS << " };\n\n";
467972a253aSDimitry Andric OS << " unsigned Index = Prop.ParameterTableOffset;\n";
468972a253aSDimitry Andric OS << " return DXILOpParameterKindTable + Index;\n";
469972a253aSDimitry Andric OS << "}\n ";
47081ad6265SDimitry Andric }
47181ad6265SDimitry Andric
472*0fca6ea1SDimitry Andric /// Entry function call that invokes the functionality of this TableGen backend
473*0fca6ea1SDimitry Andric /// \param Records TableGen records of DXIL Operations defined in DXIL.td
474*0fca6ea1SDimitry Andric /// \param OS output stream
EmitDXILOperation(RecordKeeper & Records,raw_ostream & OS)47506c3fb27SDimitry Andric static void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
47681ad6265SDimitry Andric OS << "// Generated code, do not edit.\n";
47781ad6265SDimitry Andric OS << "\n";
478*0fca6ea1SDimitry Andric // Get all DXIL Ops to intrinsic mapping records
479*0fca6ea1SDimitry Andric std::vector<Record *> OpIntrMaps =
480*0fca6ea1SDimitry Andric Records.getAllDerivedDefinitions("DXILOpMapping");
481*0fca6ea1SDimitry Andric std::vector<DXILOperationDesc> DXILOps;
482*0fca6ea1SDimitry Andric for (auto *Record : OpIntrMaps) {
483*0fca6ea1SDimitry Andric DXILOps.emplace_back(DXILOperationDesc(Record));
48481ad6265SDimitry Andric }
48581ad6265SDimitry Andric OS << "#ifdef DXIL_OP_ENUM\n";
48681ad6265SDimitry Andric emitDXILEnums(DXILOps, OS);
48781ad6265SDimitry Andric OS << "#endif\n\n";
48881ad6265SDimitry Andric OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
48981ad6265SDimitry Andric emitDXILIntrinsicMap(DXILOps, OS);
49081ad6265SDimitry Andric OS << "#endif\n\n";
49181ad6265SDimitry Andric OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";
49281ad6265SDimitry Andric emitDXILOperationTable(DXILOps, OS);
49381ad6265SDimitry Andric OS << "#endif\n\n";
49481ad6265SDimitry Andric }
49581ad6265SDimitry Andric
49606c3fb27SDimitry Andric static TableGen::Emitter::Opt X("gen-dxil-operation", EmitDXILOperation,
49706c3fb27SDimitry Andric "Generate DXIL operation information");
498