| //===- DXILSignatureAnalysis.cpp - Semantic signatures --------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DXILSignatureAnalysis.h" | |
| #include "DirectX.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Analysis/DXILMetadataAnalysis.h" | |
| #include "llvm/Frontend/HLSL/SemanticSignaturePacking.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/InstIterator.h" | |
| #include "llvm/IR/IntrinsicInst.h" | |
| #include "llvm/IR/IntrinsicsDirectX.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/MC/DXContainerPSVInfo.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <array> | |
| #include <set> | |
| using namespace llvm; | |
| using namespace llvm::dxil; | |
| using namespace llvm::hlsl; | |
| static Error signatureError(const Twine &Message) { | |
| return createStringError(inconvertibleErrorCode(), Message); | |
| } | |
| static bool isSignatureAccess(const IntrinsicInst &I) { | |
| return I.getIntrinsicID() == Intrinsic::dx_load_input || | |
| I.getIntrinsicID() == Intrinsic::dx_store_output; | |
| } | |
| // Validate widths before fromMetadata extracts integer values or narrows them. | |
| static Error checkElementNode(const MDNode *Node) { | |
| if (!Node || Node->getNumOperands() != 13) | |
| return signatureError("expected a 13-operand signature element"); | |
| for (unsigned I = 0; I != 13; ++I) { | |
| if (I == 1 || I == 4) | |
| continue; | |
| auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Node->getOperand(I)); | |
| unsigned Width = (I == 7 || (I >= 9 && I <= 11)) ? 8 : 32; | |
| if (!CI || CI->getBitWidth() != Width) | |
| return signatureError("expected i" + Twine(Width) + " at operand " + | |
| Twine(I)); | |
| } | |
| if (!isa_and_nonnull<MDString>(Node->getOperand(1))) | |
| return signatureError("expected semantic name string"); | |
| auto *Indices = dyn_cast_or_null<MDNode>(Node->getOperand(4)); | |
| if (!Indices) | |
| return signatureError("expected semantic indices node"); | |
| for (const MDOperand &Index : Indices->operands()) { | |
| auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Index); | |
| if (!CI || CI->getBitWidth() != 32) | |
| return signatureError("expected i32 semantic index"); | |
| } | |
| return Error::success(); | |
| } | |
| static bool isIntegerComponent(ElementType Type) { | |
| return Type == ElementType::I1 || Type == ElementType::I16 || | |
| Type == ElementType::U16 || Type == ElementType::I32 || | |
| Type == ElementType::U32; | |
| } | |
| static Error validateSemanticType(const SemanticSignatureElement &E) { | |
| switch (E.SemanticKind) { | |
| case dxbc::PSV::SemanticKind::Position: | |
| if (E.Rows != 1 || E.Cols != 4 || | |
| (E.CompType != ElementType::F16 && E.CompType != ElementType::F32)) | |
| return signatureError( | |
| "SV_Position requires one row of four floating-point components"); | |
| break; | |
| case dxbc::PSV::SemanticKind::VertexID: | |
| if (E.Rows != 1 || E.Cols != 1 || | |
| (E.CompType != ElementType::I16 && E.CompType != ElementType::U16 && | |
| E.CompType != ElementType::I32 && E.CompType != ElementType::U32)) | |
| return signatureError( | |
| "SV_VertexID requires a scalar 16- or 32-bit integer"); | |
| break; | |
| case dxbc::PSV::SemanticKind::InstanceID: | |
| if (E.Rows != 1 || E.Cols != 1 || | |
| (E.CompType != ElementType::U16 && E.CompType != ElementType::U32)) | |
| return signatureError( | |
| "SV_InstanceID requires a scalar 16- or 32-bit unsigned integer"); | |
| break; | |
| default: | |
| break; | |
| } | |
| return Error::success(); | |
| } | |
| static Error validateSemanticIndex(const SemanticSignatureElement &E) { | |
| switch (E.SemanticKind) { | |
| case dxbc::PSV::SemanticKind::Target: | |
| if (E.Rows != 1 || E.SemanticIndices.front() >= 8) | |
| return signatureError( | |
| "pixel targets require one row and an index within 0-7"); | |
| break; | |
| case dxbc::PSV::SemanticKind::VertexID: | |
| if (E.SemanticIndices.front() != 0) | |
| return signatureError("SV_VertexID requires semantic index 0"); | |
| break; | |
| default: | |
| break; | |
| } | |
| return Error::success(); | |
| } | |
| static Error parseSignature(Metadata *MD, | |
| SmallVectorImpl<SemanticSignatureElement> &Out, | |
| ModuleSignatureInfo &Info, | |
| Triple::EnvironmentType Stage, IOType IO, | |
| bool Native16, unsigned &Extent) { | |
| if (!MD) | |
| return Error::success(); | |
| auto *List = dyn_cast<MDNode>(MD); | |
| if (!List) | |
| return signatureError("expected a signature element list or null"); | |
| if (List->getNumOperands() > 128) | |
| return signatureError("too many signature elements"); | |
| std::set<std::pair<std::string, uint32_t>> Semantics; | |
| std::array<uint8_t, MaxSignatureRows> AllocatedColumns = {}; | |
| std::array<dxbc::PSV::InterpolationMode, MaxSignatureRows> RowModes = {}; | |
| std::array<bool, MaxSignatureRows> RowNative16 = {}; | |
| for (const MDOperand &Op : List->operands()) { | |
| auto *Node = dyn_cast_or_null<MDNode>(Op); | |
| if (Error Err = checkElementNode(Node)) | |
| return Err; | |
| auto Parsed = SemanticSignatureElement::fromMetadata(Node); | |
| if (!Parsed) | |
| return Parsed.takeError(); | |
| SemanticSignatureElement E = std::move(*Parsed); | |
| if (E.SigId != Out.size()) | |
| return signatureError("signature IDs must be dense and in list order"); | |
| if (Stage != Triple::Vertex && Stage != Triple::Pixel) | |
| return signatureError("nonempty signatures are currently supported only " | |
| "for vertex and pixel entries"); | |
| if (!E.Rows || E.Rows > MaxSignatureRows) | |
| return signatureError("signature row count must be within 1-32"); | |
| if (E.SemanticName.empty() || E.SemanticName.contains('\0')) | |
| return signatureError("invalid semantic name"); | |
| if (E.CompType != ElementType::F16 && E.CompType != ElementType::F32 && | |
| !isIntegerComponent(E.CompType)) | |
| return signatureError("unsupported signature component type"); | |
| if (E.GSStream != 0) | |
| return signatureError("nonzero stream requires a geometry output"); | |
| auto NamedKind = getSemanticKind(E.SemanticName); | |
| auto Interpretation = getInterpretationKind(NamedKind, Stage, IO); | |
| if (Interpretation == SemanticInterpretation::Invalid) | |
| return signatureError("invalid or unsupported semantic for this stage " | |
| "and signature category"); | |
| if (Interpretation == SemanticInterpretation::NotAllocated) | |
| return signatureError("semantic accessed by a dedicated intrinsic must " | |
| "not appear in an input/output signature"); | |
| auto ExpectedKind = Interpretation == SemanticInterpretation::Arbitrary | |
| ? dxbc::PSV::SemanticKind::Arbitrary | |
| : NamedKind; | |
| if (E.SemanticKind != ExpectedKind) | |
| return signatureError("semantic name and kind disagree at this signature " | |
| "point"); | |
| if (!E.isAllocated()) | |
| return signatureError("signature element " + Twine(E.SigId) + " ('" + | |
| E.SemanticName + "') has no allocated location"); | |
| if (E.StartRow >= MaxSignatureRows || | |
| E.Rows > MaxSignatureRows - E.StartRow || E.StartCol + E.Cols > 4) | |
| return signatureError( | |
| "allocated signature element exceeds register bounds"); | |
| if (E.InterpMode == dxbc::PSV::InterpolationMode::Invalid) | |
| return signatureError("invalid interpolation mode"); | |
| for (uint32_t Index : E.SemanticIndices) | |
| if (!Semantics.emplace(E.SemanticName.upper(), Index).second) | |
| return signatureError("duplicate semantic name and index"); | |
| if (Error Err = validateSemanticType(E)) | |
| return Err; | |
| if (Error Err = validateSemanticIndex(E)) | |
| return Err; | |
| if (Stage == Triple::Vertex && IO == IOType::In && | |
| (E.StartRow != Extent || E.StartCol != 0)) | |
| return signatureError("vertex inputs require stacked locations in " | |
| "signature order (element " + | |
| Twine(E.SigId) + ")"); | |
| if (Interpretation == SemanticInterpretation::Target && | |
| (E.StartRow != E.SemanticIndices.front() || E.StartCol != 0)) | |
| return signatureError("pixel target location must match its semantic " | |
| "index at column 0"); | |
| if (Stage == Triple::Pixel && IO == IOType::In && | |
| isIntegerComponent(E.CompType) && | |
| E.InterpMode != dxbc::PSV::InterpolationMode::Constant) | |
| return signatureError( | |
| "integer pixel inputs require constant interpolation"); | |
| // Co-packed elements must obey row constraints, even when their component | |
| // masks do not overlap. Min-precision types occupy 32-bit components. | |
| bool IsNative16 = Native16 && (E.CompType == ElementType::F16 || | |
| E.CompType == ElementType::I16 || | |
| E.CompType == ElementType::U16); | |
| uint8_t Mask = E.getDeclaredMask(); | |
| for (unsigned Row = E.StartRow; Row != E.StartRow + E.Rows; ++Row) { | |
| if (AllocatedColumns[Row] & Mask) | |
| return signatureError("signature elements overlap at row " + | |
| Twine(Row) + " (element " + Twine(E.SigId) + ")"); | |
| if (AllocatedColumns[Row] && RowModes[Row] != E.InterpMode) | |
| return signatureError("signature elements have different interpolation " | |
| "modes at row " + | |
| Twine(Row) + " (element " + Twine(E.SigId) + ")"); | |
| if (AllocatedColumns[Row] && RowNative16[Row] != IsNative16) | |
| return signatureError("signature elements have different component " | |
| "widths at row " + | |
| Twine(Row) + " (element " + Twine(E.SigId) + ")"); | |
| AllocatedColumns[Row] |= Mask; | |
| RowModes[Row] = E.InterpMode; | |
| RowNative16[Row] = IsNative16; | |
| } | |
| Extent = std::max(Extent, E.StartRow + E.Rows); | |
| E.SemanticName = Info.Names.insert(E.SemanticName).first->getKey(); | |
| // Access masks will be recomputed so reset to 0 here. | |
| E.UsageMask = 0; | |
| E.DynIndexMask = 0; | |
| Out.push_back(std::move(E)); | |
| } | |
| return Error::success(); | |
| } | |
| static Error analyzeAccess(const IntrinsicInst &I, EntrySignature &Sig) { | |
| bool Input = I.getIntrinsicID() == Intrinsic::dx_load_input; | |
| auto &Elements = Input ? Sig.Inputs : Sig.Outputs; | |
| auto *ID = dyn_cast<ConstantInt>(I.getArgOperand(0)); | |
| auto *Col = dyn_cast<ConstantInt>(I.getArgOperand(2)); | |
| StringRef Category = Input ? "input" : "output"; | |
| if (!ID) | |
| return signatureError(Category + | |
| " signature: signature access requires a constant " | |
| "element ID"); | |
| if (ID->getZExtValue() >= Elements.size()) | |
| return signatureError(Category + | |
| " signature: signature access has an invalid element " | |
| "ID " + | |
| Twine(ID->getZExtValue())); | |
| auto &E = Elements[ID->getZExtValue()]; | |
| auto AccessError = [&](const Twine &Message) { | |
| return signatureError(Category + " signature element " + Twine(E.SigId) + | |
| " ('" + E.SemanticName + "'): " + Message); | |
| }; | |
| Type *Ty = Input ? I.getType() : I.getArgOperand(3)->getType(); | |
| unsigned Width = 1; | |
| if (auto *VT = dyn_cast<FixedVectorType>(Ty)) { | |
| Width = VT->getNumElements(); | |
| Ty = VT->getElementType(); | |
| } | |
| if (!Col || Col->getZExtValue() >= E.Cols || | |
| Width > E.Cols - Col->getZExtValue()) | |
| return AccessError("signature access has an invalid component index"); | |
| auto *Row = dyn_cast<ConstantInt>(I.getArgOperand(1)); | |
| if (Row && Row->getZExtValue() >= E.Rows) | |
| return AccessError("signature access has an invalid row index"); | |
| if (!Row && E.Rows == 1) | |
| return AccessError( | |
| "dynamic indexing requires a multi-row signature element"); | |
| bool CorrectType = false; | |
| switch (E.CompType) { | |
| case ElementType::F16: | |
| CorrectType = Ty->isHalfTy(); | |
| break; | |
| case ElementType::F32: | |
| CorrectType = Ty->isFloatTy(); | |
| break; | |
| case ElementType::I16: | |
| case ElementType::U16: | |
| CorrectType = Ty->isIntegerTy(16); | |
| break; | |
| case ElementType::I1: | |
| // Boolean signature elements use the i32 load/store overload. | |
| CorrectType = Ty->isIntegerTy(32); | |
| break; | |
| case ElementType::I32: | |
| case ElementType::U32: | |
| CorrectType = Ty->isIntegerTy(32); | |
| break; | |
| default: | |
| llvm_unreachable("invalid signature component type"); | |
| } | |
| if (!CorrectType) | |
| return AccessError("signature access type disagrees with its element"); | |
| uint8_t Mask = ((1U << Width) - 1) << Col->getZExtValue(); | |
| E.UsageMask |= Mask << E.StartCol; | |
| if (!Row) | |
| E.DynIndexMask |= Mask; | |
| return Error::success(); | |
| } | |
| static void buildDependencyMap(EntrySignature &Sig) { | |
| // Each output vector needs four component bits, so one 32-bit word holds | |
| // eight vectors. Adding seven rounds up to include a partially filled word. | |
| unsigned Words = (Sig.OutputVectors + 7) / 8; | |
| Sig.InputOutputMap.assign(Sig.InputVectors * 4 * Words, 0); | |
| // Conservatively connect every accessed input component to every written | |
| // output component. This includes control, memory, call, and dynamic-index | |
| // dependencies without incorrectly claiming that unknown dependencies are | |
| // absent. Unused components and padding remain zero. Both DXIL and PSV use | |
| // this same map; a more precise dependency analysis can refine it later. | |
| for (const auto &Input : Sig.Inputs) | |
| for (unsigned IR = 0; IR != Input.Rows; ++IR) | |
| for (unsigned IC = 0; IC != 4; ++IC) { | |
| if (!(Input.UsageMask & (1U << IC))) | |
| continue; | |
| unsigned Base = ((Input.StartRow + IR) * 4 + IC) * Words; | |
| for (const auto &Output : Sig.Outputs) | |
| for (unsigned OR = 0; OR != Output.Rows; ++OR) | |
| for (unsigned OC = 0; OC != 4; ++OC) { | |
| if (!(Output.UsageMask & (1U << OC))) | |
| continue; | |
| unsigned Bit = (Output.StartRow + OR) * 4 + OC; | |
| Sig.InputOutputMap[Base + Bit / 32] |= 1U << (Bit % 32); | |
| } | |
| } | |
| } | |
| static Expected<ModuleSignatureInfo> | |
| analyzeModule(Module &M, const ModuleMetadataInfo &MMI) { | |
| ModuleSignatureInfo Info; | |
| if (NamedMDNode *Table = M.getNamedMetadata("dx.semantic.signatures")) { | |
| for (const MDNode *Record : Table->operands()) { | |
| if (Record->getNumOperands() != 3) | |
| return signatureError( | |
| "expected an entry/input/output signature triple"); | |
| auto *VAM = dyn_cast_or_null<ValueAsMetadata>(Record->getOperand(0)); | |
| auto *F = VAM ? dyn_cast<Function>(VAM->getValue()) : nullptr; | |
| if (!F || F->getParent() != &M || F->isDeclaration()) | |
| return signatureError( | |
| "signature entry must be a defined function in the module"); | |
| auto EP = | |
| llvm::find_if(MMI.EntryPropertyVec, | |
| [F](const EntryProperties &E) { return E.Entry == F; }); | |
| if (EP == MMI.EntryPropertyVec.end()) | |
| return signatureError("signature function is not a shader entry"); | |
| if (Info.Entries.contains(F)) | |
| return signatureError("duplicate signature record for entry '" + | |
| F->getName() + "'"); | |
| EntrySignature Sig; | |
| Sig.Stage = EP->ShaderStage; | |
| if (auto *Native = mdconst::dyn_extract_or_null<ConstantInt>( | |
| M.getModuleFlag("dx.nativelowprec"))) | |
| Sig.UseNative16Bit = !Native->isZero(); | |
| if (Sig.UseNative16Bit && MMI.ShaderModelVersion < VersionTuple(6, 2)) | |
| return signatureError( | |
| "native 16-bit signatures require shader model 6.2"); | |
| auto AddContext = [&](Error Err, StringRef Category) -> Error { | |
| return signatureError("entry '" + F->getName() + "' " + Category + | |
| " signature: " + toString(std::move(Err))); | |
| }; | |
| if (Error Err = | |
| parseSignature(Record->getOperand(1), Sig.Inputs, Info, Sig.Stage, | |
| IOType::In, Sig.UseNative16Bit, Sig.InputVectors)) | |
| return AddContext(std::move(Err), "input"); | |
| if (Error Err = parseSignature(Record->getOperand(2), Sig.Outputs, Info, | |
| Sig.Stage, IOType::Out, Sig.UseNative16Bit, | |
| Sig.OutputVectors)) | |
| return AddContext(std::move(Err), "output"); | |
| for (const Instruction &I : instructions(F)) | |
| if (auto *II = dyn_cast<IntrinsicInst>(&I); | |
| II && isSignatureAccess(*II)) | |
| if (Error Err = analyzeAccess(*II, Sig)) | |
| return signatureError("entry '" + F->getName() + | |
| "': " + toString(std::move(Err))); | |
| buildDependencyMap(Sig); | |
| Info.Entries.try_emplace(F, std::move(Sig)); | |
| } | |
| } | |
| // Signature IDs are local to an entry. Do not guess which entry a helper's | |
| // accesses belong to, even if that helper happens to have a single caller. | |
| for (const Function &F : M) | |
| if (!Info.Entries.contains(&F)) | |
| for (const Instruction &I : instructions(F)) | |
| if (auto *II = dyn_cast<IntrinsicInst>(&I); | |
| II && isSignatureAccess(*II)) | |
| return signatureError("signature access in function '" + F.getName() + | |
| "' has no entry signature metadata"); | |
| return std::move(Info); | |
| } | |
| static ModuleSignatureInfo collectSignatures(Module &M, | |
| const ModuleMetadataInfo &MMI) { | |
| auto Result = analyzeModule(M, MMI); | |
| if (!Result) { | |
| M.getContext().emitError("Invalid semantic signature: " + | |
| toString(Result.takeError())); | |
| return ModuleSignatureInfo(); | |
| } | |
| return std::move(*Result); | |
| } | |
| static MDNode *emitSignature(LLVMContext &Ctx, | |
| ArrayRef<SemanticSignatureElement> Elements, | |
| VersionTuple ValidatorVersion) { | |
| if (Elements.empty()) | |
| return nullptr; | |
| auto I32 = [&](uint32_t V) { | |
| return ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Ctx), V)); | |
| }; | |
| auto I8 = [&](uint8_t V) { | |
| return ConstantAsMetadata::get(ConstantInt::get(Type::getInt8Ty(Ctx), V)); | |
| }; | |
| SmallVector<Metadata *> Nodes; | |
| for (const auto &E : Elements) { | |
| SmallVector<Metadata *> Indices, Props; | |
| for (uint32_t Index : E.SemanticIndices) | |
| Indices.push_back(I32(Index)); | |
| if (E.GSStream) | |
| Props.append({I32(0), I32(E.GSStream)}); | |
| if (E.DynIndexMask) | |
| Props.append({I32(2), I32(E.DynIndexMask)}); | |
| if (E.UsageMask && | |
| (ValidatorVersion.empty() || ValidatorVersion == VersionTuple(0, 0) || | |
| ValidatorVersion >= VersionTuple(1, 5))) | |
| Props.append({I32(3), I32(E.UsageMask >> E.StartCol)}); | |
| Nodes.push_back(MDNode::get( | |
| Ctx, {I32(E.SigId), MDString::get(Ctx, E.SemanticName), | |
| I8(static_cast<uint8_t>(E.CompType)), | |
| I8(static_cast<uint8_t>(E.SemanticKind)), | |
| MDNode::get(Ctx, Indices), I8(static_cast<uint8_t>(E.InterpMode)), | |
| I32(E.Rows), I8(E.Cols), I32(E.StartRow), I8(E.StartCol), | |
| Props.empty() ? nullptr : MDNode::get(Ctx, Props)})); | |
| } | |
| return MDNode::get(Ctx, Nodes); | |
| } | |
| static dxbc::SigComponentType getComponentType(ElementType Type, bool Legacy) { | |
| switch (Type) { | |
| case ElementType::I1: | |
| return Legacy ? dxbc::SigComponentType::Unknown | |
| : dxbc::SigComponentType::UInt32; | |
| case ElementType::I16: | |
| return dxbc::SigComponentType::SInt16; | |
| case ElementType::U16: | |
| return dxbc::SigComponentType::UInt16; | |
| case ElementType::I32: | |
| return dxbc::SigComponentType::SInt32; | |
| case ElementType::U32: | |
| return dxbc::SigComponentType::UInt32; | |
| case ElementType::F16: | |
| return dxbc::SigComponentType::Float16; | |
| case ElementType::F32: | |
| return dxbc::SigComponentType::Float32; | |
| default: | |
| llvm_unreachable("invalid signature component type"); | |
| } | |
| } | |
| static dxbc::D3DSystemValue getSystemValue(dxbc::PSV::SemanticKind Kind) { | |
| using KindTy = dxbc::PSV::SemanticKind; | |
| switch (Kind) { | |
| case KindTy::Arbitrary: | |
| return dxbc::D3DSystemValue::Undefined; | |
| case KindTy::VertexID: | |
| return dxbc::D3DSystemValue::VertexID; | |
| case KindTy::InstanceID: | |
| return dxbc::D3DSystemValue::InstanceID; | |
| case KindTy::Position: | |
| return dxbc::D3DSystemValue::Position; | |
| case KindTy::IsFrontFace: | |
| return dxbc::D3DSystemValue::IsFrontFace; | |
| case KindTy::ClipDistance: | |
| return dxbc::D3DSystemValue::ClipDistance; | |
| case KindTy::CullDistance: | |
| return dxbc::D3DSystemValue::CullDistance; | |
| case KindTy::Target: | |
| return dxbc::D3DSystemValue::Target; | |
| default: | |
| llvm_unreachable("validated vertex/pixel signature semantic"); | |
| } | |
| } | |
| // The DXIL reader normalizes system-value semantic names when loading module | |
| // metadata. The validator then recreates ISG1/OSG1 from those normalized names | |
| // and compares the parts byte-for-byte, so the container must use the same | |
| // spelling even if HLSL (or the source metadata) used a different casing. | |
| static StringRef getContainerSemanticName(const SemanticSignatureElement &E) { | |
| using Kind = dxbc::PSV::SemanticKind; | |
| switch (E.SemanticKind) { | |
| case Kind::Arbitrary: | |
| return E.SemanticName; | |
| case Kind::VertexID: | |
| return "SV_VertexID"; | |
| case Kind::InstanceID: | |
| return "SV_InstanceID"; | |
| case Kind::Position: | |
| return "SV_Position"; | |
| case Kind::IsFrontFace: | |
| return "SV_IsFrontFace"; | |
| case Kind::ClipDistance: | |
| return "SV_ClipDistance"; | |
| case Kind::CullDistance: | |
| return "SV_CullDistance"; | |
| case Kind::Target: | |
| return "SV_Target"; | |
| default: | |
| llvm_unreachable("validated vertex/pixel signature semantic"); | |
| } | |
| } | |
| static void buildSignature(mcdxbc::Signature &Sig, | |
| ArrayRef<SemanticSignatureElement> Elements, | |
| bool Input, bool Native16, bool Legacy) { | |
| for (const auto &E : Elements) { | |
| uint8_t Mask = E.getDeclaredMask(); | |
| uint8_t ExclusiveMask = | |
| Input ? E.getAlwaysReadsMask() : E.getNeverWritesMask(); | |
| if (Legacy) | |
| ExclusiveMask = Input ? 0 : static_cast<uint8_t>(~Mask); | |
| for (auto [Row, Index] : enumerate(E.SemanticIndices)) | |
| Sig.addParam(E.GSStream, getContainerSemanticName(E), Index, | |
| getSystemValue(E.SemanticKind), | |
| getComponentType(E.CompType, Legacy), E.StartRow + Row, Mask, | |
| ExclusiveMask, E.getMinPrecision(!Native16)); | |
| } | |
| } | |
| static void buildPSVElements(SmallVectorImpl<mcdxbc::PSVSignatureElement> &Dest, | |
| ArrayRef<SemanticSignatureElement> Elements, | |
| bool Legacy) { | |
| for (const auto &E : Elements) { | |
| auto Type = getComponentType(E.CompType, Legacy); | |
| // SigComponentType and PSV ComponentType share COMPONENT_TYPE definitions. | |
| Dest.push_back({E.SemanticKind == dxbc::PSV::SemanticKind::Arbitrary | |
| ? E.SemanticName | |
| : StringRef(), | |
| E.SemanticIndices, static_cast<uint8_t>(E.StartRow), E.Cols, | |
| E.StartCol, E.isAllocated(), E.SemanticKind, | |
| static_cast<dxbc::PSV::ComponentType>(Type), E.InterpMode, | |
| E.DynIndexMask, static_cast<uint8_t>(E.GSStream)}); | |
| } | |
| } | |
| MDTuple *EntrySignature::getAsMetadata(LLVMContext &Ctx, | |
| VersionTuple ValidatorVersion) const { | |
| if (Inputs.empty() && Outputs.empty()) | |
| return nullptr; | |
| return MDNode::get(Ctx, | |
| {emitSignature(Ctx, Inputs, ValidatorVersion), | |
| emitSignature(Ctx, Outputs, ValidatorVersion), nullptr}); | |
| } | |
| void EntrySignature::buildSignatures(mcdxbc::Signature &Input, | |
| mcdxbc::Signature &Output, | |
| VersionTuple ValidatorVersion) const { | |
| bool Legacy = !ValidatorVersion.empty() && | |
| ValidatorVersion != VersionTuple(0, 0) && | |
| ValidatorVersion < VersionTuple(1, 5); | |
| buildSignature(Input, Inputs, true, UseNative16Bit, Legacy); | |
| buildSignature(Output, Outputs, false, UseNative16Bit, Legacy); | |
| } | |
| void EntrySignature::updatePSV(mcdxbc::PSVRuntimeInfo &PSV, | |
| VersionTuple ValidatorVersion) const { | |
| bool Legacy = !ValidatorVersion.empty() && | |
| ValidatorVersion != VersionTuple(0, 0) && | |
| ValidatorVersion < VersionTuple(1, 5); | |
| buildPSVElements(PSV.InputElements, Inputs, Legacy); | |
| buildPSVElements(PSV.OutputElements, Outputs, Legacy); | |
| PSV.BaseData.SigInputVectors = InputVectors; | |
| PSV.BaseData.SigOutputVectors[0] = OutputVectors; | |
| PSV.InputOutputMap[0] = InputOutputMap; | |
| if (Stage == Triple::Vertex) | |
| PSV.BaseData.StageInfo.VS.OutputPositionPresent = | |
| llvm::any_of(Outputs, [](const auto &E) { | |
| return E.SemanticKind == dxbc::PSV::SemanticKind::Position; | |
| }); | |
| if (Stage == Triple::Pixel) | |
| PSV.BaseData.StageInfo.PS.SampleFrequency = | |
| llvm::any_of(Inputs, [](const auto &E) { | |
| return E.InterpMode == dxbc::PSV::InterpolationMode::LinearSample || | |
| E.InterpMode == | |
| dxbc::PSV::InterpolationMode::LinearNoperspectiveSample; | |
| }); | |
| } | |
| SmallVector<uint32_t> EntrySignature::getDependencyState() const { | |
| SmallVector<uint32_t> State = {InputVectors * 4, OutputVectors * 4}; | |
| llvm::append_range(State, InputOutputMap); | |
| return State; | |
| } | |
| void EntrySignature::print(raw_ostream &OS) const { | |
| auto Print = [&](StringRef Name, ArrayRef<SemanticSignatureElement> Elements, | |
| unsigned Vectors) { | |
| OS << " " << Name << ": " << Elements.size() << " elements, " << Vectors | |
| << " vectors\n"; | |
| for (const auto &E : Elements) | |
| OS << " " << E.SigId << ": " << E.SemanticName << " rows=" << E.Rows | |
| << " cols=" << unsigned(E.Cols) << " at " << E.StartRow << ":" | |
| << unsigned(E.StartCol) << " usage=" << unsigned(E.UsageMask) | |
| << " dynamic=" << unsigned(E.DynIndexMask) << '\n'; | |
| }; | |
| Print("Inputs", Inputs, InputVectors); | |
| Print("Outputs", Outputs, OutputVectors); | |
| } | |
| void ModuleSignatureInfo::print(raw_ostream &OS, const Module &M) const { | |
| for (const Function &F : M) | |
| if (const auto *Sig = get(&F)) { | |
| OS << "Semantic signatures for '" << F.getName() << "':\n"; | |
| Sig->print(OS); | |
| } | |
| } | |
| AnalysisKey SignatureAnalysis::Key; | |
| ModuleSignatureInfo SignatureAnalysis::run(Module &M, | |
| ModuleAnalysisManager &AM) { | |
| return collectSignatures(M, AM.getResult<DXILMetadataAnalysis>(M)); | |
| } | |
| PreservedAnalyses SignatureAnalysisPrinter::run(Module &M, | |
| ModuleAnalysisManager &AM) { | |
| AM.getResult<SignatureAnalysis>(M).print(OS, M); | |
| return PreservedAnalyses::all(); | |
| } | |
| bool SignatureAnalysisWrapper::runOnModule(Module &M) { | |
| Info = std::make_unique<ModuleSignatureInfo>(collectSignatures( | |
| M, getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata())); | |
| return false; | |
| } | |
| void SignatureAnalysisWrapper::getAnalysisUsage(AnalysisUsage &AU) const { | |
| AU.setPreservesAll(); | |
| AU.addRequired<DXILMetadataAnalysisWrapperPass>(); | |
| } | |
| void SignatureAnalysisWrapper::print(raw_ostream &OS, const Module *M) const { | |
| if (Info) | |
| Info->print(OS, *M); | |
| } | |
| char SignatureAnalysisWrapper::ID; | |
| INITIALIZE_PASS_BEGIN(SignatureAnalysisWrapper, "dxil-signature-analysis", | |
| "DXIL Semantic Signature Analysis", false, true) | |
| INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass) | |
| INITIALIZE_PASS_END(SignatureAnalysisWrapper, "dxil-signature-analysis", | |
| "DXIL Semantic Signature Analysis", false, true) |