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 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 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 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 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 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 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 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 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 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