Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/DXILEmitter.cpp
35258 views
//===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// DXILEmitter uses the descriptions of DXIL operation to construct enum and9// helper functions for DXIL operation.10//11//===----------------------------------------------------------------------===//1213#include "Basic/SequenceToOffsetTable.h"14#include "Common/CodeGenTarget.h"15#include "llvm/ADT/STLExtras.h"16#include "llvm/ADT/SmallSet.h"17#include "llvm/ADT/SmallVector.h"18#include "llvm/ADT/StringSet.h"19#include "llvm/ADT/StringSwitch.h"20#include "llvm/CodeGenTypes/MachineValueType.h"21#include "llvm/Support/DXILABI.h"22#include "llvm/TableGen/Record.h"23#include "llvm/TableGen/TableGenBackend.h"24#include <string>2526using namespace llvm;27using namespace llvm::dxil;2829namespace {3031struct DXILShaderModel {32int Major = 0;33int Minor = 0;34};3536struct DXILOperationDesc {37std::string OpName; // name of DXIL operation38int OpCode; // ID of DXIL operation39StringRef OpClass; // name of the opcode class40StringRef Doc; // the documentation description of this instruction41SmallVector<Record *> OpTypes; // Vector of operand type records -42// return type is at index 043SmallVector<std::string>44OpAttributes; // operation attribute represented as strings45StringRef Intrinsic; // The llvm intrinsic map to OpName. Default is "" which46// means no map exists47bool IsDeriv = false; // whether this is some kind of derivative48bool IsGradient = false; // whether this requires a gradient calculation49bool IsFeedback = false; // whether this is a sampler feedback op50bool IsWave =51false; // whether this requires in-wave, cross-lane functionality52bool RequiresUniformInputs = false; // whether this operation requires that53// all of its inputs are uniform across54// the wave55SmallVector<StringRef, 4>56ShaderStages; // shader stages to which this applies, empty for all.57DXILShaderModel ShaderModel; // minimum shader model required58DXILShaderModel ShaderModelTranslated; // minimum shader model required with59// translation by linker60int OverloadParamIndex; // Index of parameter with overload type.61// -1 : no overload types62SmallVector<StringRef, 4> counters; // counters for this inst.63DXILOperationDesc(const Record *);64};65} // end anonymous namespace6667/// Return dxil::ParameterKind corresponding to input LLVMType record68///69/// \param R TableGen def record of class LLVMType70/// \return ParameterKind As defined in llvm/Support/DXILABI.h7172static ParameterKind getParameterKind(const Record *R) {73auto VTRec = R->getValueAsDef("VT");74switch (getValueType(VTRec)) {75case MVT::isVoid:76return ParameterKind::Void;77case MVT::f16:78return ParameterKind::Half;79case MVT::f32:80return ParameterKind::Float;81case MVT::f64:82return ParameterKind::Double;83case MVT::i1:84return ParameterKind::I1;85case MVT::i8:86return ParameterKind::I8;87case MVT::i16:88return ParameterKind::I16;89case MVT::i32:90return ParameterKind::I32;91case MVT::fAny:92case MVT::iAny:93return ParameterKind::Overload;94case MVT::Other:95// Handle DXIL-specific overload types96if (R->getValueAsInt("isHalfOrFloat") || R->getValueAsInt("isI16OrI32")) {97return ParameterKind::Overload;98}99[[fallthrough]];100default:101llvm_unreachable("Support for specified DXIL Type not yet implemented");102}103}104105/// Construct an object using the DXIL Operation records specified106/// in DXIL.td. This serves as the single source of reference of107/// the information extracted from the specified Record R, for108/// C++ code generated by this TableGen backend.109// \param R Object representing TableGen record of a DXIL Operation110DXILOperationDesc::DXILOperationDesc(const Record *R) {111OpName = R->getNameInitAsString();112OpCode = R->getValueAsInt("OpCode");113114Doc = R->getValueAsString("Doc");115116auto TypeRecs = R->getValueAsListOfDefs("OpTypes");117unsigned TypeRecsSize = TypeRecs.size();118// Populate OpTypes with return type and parameter types119120// Parameter indices of overloaded parameters.121// This vector contains overload parameters in the order used to122// resolve an LLVMMatchType in accordance with convention outlined in123// the comment before the definition of class LLVMMatchType in124// llvm/IR/Intrinsics.td125SmallVector<int> OverloadParamIndices;126for (unsigned i = 0; i < TypeRecsSize; i++) {127auto TR = TypeRecs[i];128// Track operation parameter indices of any overload types129auto isAny = TR->getValueAsInt("isAny");130if (isAny == 1) {131// TODO: At present it is expected that all overload types in a DXIL Op132// are of the same type. Hence, OverloadParamIndices will have only one133// element. This implies we do not need a vector. However, until more134// (all?) DXIL Ops are added in DXIL.td, a vector is being used to flag135// cases this assumption would not hold.136if (!OverloadParamIndices.empty()) {137bool knownType = true;138// Ensure that the same overload type registered earlier is being used139for (auto Idx : OverloadParamIndices) {140if (TR != TypeRecs[Idx]) {141knownType = false;142break;143}144}145if (!knownType) {146report_fatal_error("Specification of multiple differing overload "147"parameter types not yet supported",148false);149}150} else {151OverloadParamIndices.push_back(i);152}153}154// Populate OpTypes array according to the type specification155if (TR->isAnonymous()) {156// Check prior overload types exist157assert(!OverloadParamIndices.empty() &&158"No prior overloaded parameter found to match.");159// Get the parameter index of anonymous type, TR, references160auto OLParamIndex = TR->getValueAsInt("Number");161// Resolve and insert the type to that at OLParamIndex162OpTypes.emplace_back(TypeRecs[OLParamIndex]);163} else {164// A non-anonymous type. Just record it in OpTypes165OpTypes.emplace_back(TR);166}167}168169// Set the index of the overload parameter, if any.170OverloadParamIndex = -1; // default; indicating none171if (!OverloadParamIndices.empty()) {172if (OverloadParamIndices.size() > 1)173report_fatal_error("Multiple overload type specification not supported",174false);175OverloadParamIndex = OverloadParamIndices[0];176}177// Get the operation class178OpClass = R->getValueAsDef("OpClass")->getName();179180if (R->getValue("LLVMIntrinsic")) {181auto *IntrinsicDef = R->getValueAsDef("LLVMIntrinsic");182auto DefName = IntrinsicDef->getName();183assert(DefName.starts_with("int_") && "invalid intrinsic name");184// Remove the int_ from intrinsic name.185Intrinsic = DefName.substr(4);186// TODO: For now, assume that attributes of DXIL Operation are the same as187// that of the intrinsic. Deviations are expected to be encoded in TableGen188// record specification and handled accordingly here. Support to be added189// as needed.190auto IntrPropList = IntrinsicDef->getValueAsListInit("IntrProperties");191auto IntrPropListSize = IntrPropList->size();192for (unsigned i = 0; i < IntrPropListSize; i++) {193OpAttributes.emplace_back(IntrPropList->getElement(i)->getAsString());194}195}196}197198/// Return a string representation of ParameterKind enum199/// \param Kind Parameter Kind enum value200/// \return std::string string representation of input Kind201static std::string getParameterKindStr(ParameterKind Kind) {202switch (Kind) {203case ParameterKind::Invalid:204return "Invalid";205case ParameterKind::Void:206return "Void";207case ParameterKind::Half:208return "Half";209case ParameterKind::Float:210return "Float";211case ParameterKind::Double:212return "Double";213case ParameterKind::I1:214return "I1";215case ParameterKind::I8:216return "I8";217case ParameterKind::I16:218return "I16";219case ParameterKind::I32:220return "I32";221case ParameterKind::I64:222return "I64";223case ParameterKind::Overload:224return "Overload";225case ParameterKind::CBufferRet:226return "CBufferRet";227case ParameterKind::ResourceRet:228return "ResourceRet";229case ParameterKind::DXILHandle:230return "DXILHandle";231}232llvm_unreachable("Unknown llvm::dxil::ParameterKind enum");233}234235/// Return a string representation of OverloadKind enum that maps to236/// input LLVMType record237/// \param R TableGen def record of class LLVMType238/// \return std::string string representation of OverloadKind239240static std::string getOverloadKindStr(const Record *R) {241auto VTRec = R->getValueAsDef("VT");242switch (getValueType(VTRec)) {243case MVT::isVoid:244return "OverloadKind::VOID";245case MVT::f16:246return "OverloadKind::HALF";247case MVT::f32:248return "OverloadKind::FLOAT";249case MVT::f64:250return "OverloadKind::DOUBLE";251case MVT::i1:252return "OverloadKind::I1";253case MVT::i8:254return "OverloadKind::I8";255case MVT::i16:256return "OverloadKind::I16";257case MVT::i32:258return "OverloadKind::I32";259case MVT::i64:260return "OverloadKind::I64";261case MVT::iAny:262return "OverloadKind::I16 | OverloadKind::I32 | OverloadKind::I64";263case MVT::fAny:264return "OverloadKind::HALF | OverloadKind::FLOAT | OverloadKind::DOUBLE";265case MVT::Other:266// Handle DXIL-specific overload types267{268if (R->getValueAsInt("isHalfOrFloat")) {269return "OverloadKind::HALF | OverloadKind::FLOAT";270} else if (R->getValueAsInt("isI16OrI32")) {271return "OverloadKind::I16 | OverloadKind::I32";272}273}274[[fallthrough]];275default:276llvm_unreachable(277"Support for specified parameter OverloadKind not yet implemented");278}279}280281/// Emit Enums of DXIL Ops282/// \param A vector of DXIL Ops283/// \param Output stream284static void emitDXILEnums(std::vector<DXILOperationDesc> &Ops,285raw_ostream &OS) {286// Sort by OpCode287llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {288return A.OpCode < B.OpCode;289});290291OS << "// Enumeration for operations specified by DXIL\n";292OS << "enum class OpCode : unsigned {\n";293294for (auto &Op : Ops) {295// Name = ID, // Doc296OS << Op.OpName << " = " << Op.OpCode << ", // " << Op.Doc << "\n";297}298299OS << "\n};\n\n";300301OS << "// Groups for DXIL operations with equivalent function templates\n";302OS << "enum class OpCodeClass : unsigned {\n";303// Build an OpClass set to print304SmallSet<StringRef, 2> OpClassSet;305for (auto &Op : Ops) {306OpClassSet.insert(Op.OpClass);307}308for (auto &C : OpClassSet) {309OS << C << ",\n";310}311OS << "\n};\n\n";312}313314/// Emit map of DXIL operation to LLVM or DirectX intrinsic315/// \param A vector of DXIL Ops316/// \param Output stream317static void emitDXILIntrinsicMap(std::vector<DXILOperationDesc> &Ops,318raw_ostream &OS) {319OS << "\n";320// FIXME: use array instead of SmallDenseMap.321OS << "static const SmallDenseMap<Intrinsic::ID, dxil::OpCode> LowerMap = "322"{\n";323for (auto &Op : Ops) {324if (Op.Intrinsic.empty())325continue;326// {Intrinsic::sin, dxil::OpCode::Sin},327OS << " { Intrinsic::" << Op.Intrinsic << ", dxil::OpCode::" << Op.OpName328<< "},\n";329}330OS << "};\n";331OS << "\n";332}333334/// Convert operation attribute string to Attribute enum335///336/// \param Attr string reference337/// \return std::string Attribute enum string338339static std::string emitDXILOperationAttr(SmallVector<std::string> Attrs) {340for (auto Attr : Attrs) {341// TODO: For now just recognize IntrNoMem and IntrReadMem as valid and342// ignore others.343if (Attr == "IntrNoMem") {344return "Attribute::ReadNone";345} else if (Attr == "IntrReadMem") {346return "Attribute::ReadOnly";347}348}349return "Attribute::None";350}351352/// Emit DXIL operation table353/// \param A vector of DXIL Ops354/// \param Output stream355static void emitDXILOperationTable(std::vector<DXILOperationDesc> &Ops,356raw_ostream &OS) {357// Sort by OpCode.358llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {359return A.OpCode < B.OpCode;360});361362// Collect Names.363SequenceToOffsetTable<std::string> OpClassStrings;364SequenceToOffsetTable<std::string> OpStrings;365SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters;366367StringMap<SmallVector<ParameterKind>> ParameterMap;368StringSet<> ClassSet;369for (auto &Op : Ops) {370OpStrings.add(Op.OpName);371372if (ClassSet.contains(Op.OpClass))373continue;374ClassSet.insert(Op.OpClass);375OpClassStrings.add(Op.OpClass.data());376SmallVector<ParameterKind> ParamKindVec;377// ParamKindVec is a vector of parameters. Skip return type at index 0378for (unsigned i = 1; i < Op.OpTypes.size(); i++) {379ParamKindVec.emplace_back(getParameterKind(Op.OpTypes[i]));380}381ParameterMap[Op.OpClass] = ParamKindVec;382Parameters.add(ParamKindVec);383}384385// Layout names.386OpStrings.layout();387OpClassStrings.layout();388Parameters.layout();389390// Emit the DXIL operation table.391//{dxil::OpCode::Sin, OpCodeNameIndex, OpCodeClass::unary,392// OpCodeClassNameIndex,393// OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0,394// 3, ParameterTableOffset},395OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) "396"{\n";397398OS << " static const OpCodeProperty OpCodeProps[] = {\n";399for (auto &Op : Ops) {400// Consider Op.OverloadParamIndex as the overload parameter index, by401// default402auto OLParamIdx = Op.OverloadParamIndex;403// If no overload parameter index is set, treat first parameter type as404// overload type - unless the Op has no parameters, in which case treat the405// return type - as overload parameter to emit the appropriate overload kind406// enum.407if (OLParamIdx < 0) {408OLParamIdx = (Op.OpTypes.size() > 1) ? 1 : 0;409}410OS << " { dxil::OpCode::" << Op.OpName << ", " << OpStrings.get(Op.OpName)411<< ", OpCodeClass::" << Op.OpClass << ", "412<< OpClassStrings.get(Op.OpClass.data()) << ", "413<< getOverloadKindStr(Op.OpTypes[OLParamIdx]) << ", "414<< emitDXILOperationAttr(Op.OpAttributes) << ", "415<< Op.OverloadParamIndex << ", " << Op.OpTypes.size() - 1 << ", "416<< Parameters.get(ParameterMap[Op.OpClass]) << " },\n";417}418OS << " };\n";419420OS << " // FIXME: change search to indexing with\n";421OS << " // Op once all DXIL operations are added.\n";422OS << " OpCodeProperty TmpProp;\n";423OS << " TmpProp.OpCode = Op;\n";424OS << " const OpCodeProperty *Prop =\n";425OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n";426OS << " [](const OpCodeProperty &A, const "427"OpCodeProperty &B) {\n";428OS << " return A.OpCode < B.OpCode;\n";429OS << " });\n";430OS << " assert(Prop && \"failed to find OpCodeProperty\");\n";431OS << " return Prop;\n";432OS << "}\n\n";433434// Emit the string tables.435OS << "static const char *getOpCodeName(dxil::OpCode Op) {\n\n";436437OpStrings.emitStringLiteralDef(OS,438" static const char DXILOpCodeNameTable[]");439440OS << " auto *Prop = getOpCodeProperty(Op);\n";441OS << " unsigned Index = Prop->OpCodeNameOffset;\n";442OS << " return DXILOpCodeNameTable + Index;\n";443OS << "}\n\n";444445OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "446"{\n\n";447448OpClassStrings.emitStringLiteralDef(449OS, " static const char DXILOpCodeClassNameTable[]");450451OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";452OS << " return DXILOpCodeClassNameTable + Index;\n";453OS << "}\n ";454455OS << "static const ParameterKind *getOpCodeParameterKind(const "456"OpCodeProperty &Prop) "457"{\n\n";458OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n";459Parameters.emit(460OS,461[](raw_ostream &ParamOS, ParameterKind Kind) {462ParamOS << "ParameterKind::" << getParameterKindStr(Kind);463},464"ParameterKind::Invalid");465OS << " };\n\n";466OS << " unsigned Index = Prop.ParameterTableOffset;\n";467OS << " return DXILOpParameterKindTable + Index;\n";468OS << "}\n ";469}470471/// Entry function call that invokes the functionality of this TableGen backend472/// \param Records TableGen records of DXIL Operations defined in DXIL.td473/// \param OS output stream474static void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {475OS << "// Generated code, do not edit.\n";476OS << "\n";477// Get all DXIL Ops to intrinsic mapping records478std::vector<Record *> OpIntrMaps =479Records.getAllDerivedDefinitions("DXILOpMapping");480std::vector<DXILOperationDesc> DXILOps;481for (auto *Record : OpIntrMaps) {482DXILOps.emplace_back(DXILOperationDesc(Record));483}484OS << "#ifdef DXIL_OP_ENUM\n";485emitDXILEnums(DXILOps, OS);486OS << "#endif\n\n";487OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";488emitDXILIntrinsicMap(DXILOps, OS);489OS << "#endif\n\n";490OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";491emitDXILOperationTable(DXILOps, OS);492OS << "#endif\n\n";493}494495static TableGen::Emitter::Opt X("gen-dxil-operation", EmitDXILOperation,496"Generate DXIL operation information");497498499