| //===- DXContainer.h - DXContainer file implementation ----------*- C++ -*-===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file declares the DXContainerFile class, which implements the ObjectFile | |
| // interface for DXContainer files. | |
| // | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_OBJECT_DXCONTAINER_H | |
| #define LLVM_OBJECT_DXCONTAINER_H | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/BinaryFormat/DXContainer.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/MemoryBufferRef.h" | |
| #include "llvm/TargetParser/Triple.h" | |
| #include <array> | |
| #include <variant> | |
| namespace llvm { | |
| namespace object { | |
| namespace detail { | |
| template <typename T> | |
| std::enable_if_t<std::is_arithmetic<T>::value, void> swapBytes(T &value) { | |
| sys::swapByteOrder(value); | |
| } | |
| template <typename T> | |
| std::enable_if_t<std::is_class<T>::value, void> swapBytes(T &value) { | |
| value.swapBytes(); | |
| } | |
| } // namespace detail | |
| // This class provides a view into the underlying resource array. The Resource | |
| // data is little-endian encoded and may not be properly aligned to read | |
| // directly from. The dereference operator creates a copy of the data and byte | |
| // swaps it as appropriate. | |
| template <typename T> struct ViewArray { | |
| StringRef Data; | |
| uint32_t Stride = sizeof(T); // size of each element in the list. | |
| ViewArray() = default; | |
| ViewArray(StringRef D, size_t S) : Data(D), Stride(S) {} | |
| using value_type = T; | |
| static constexpr uint32_t MaxStride() { | |
| return static_cast<uint32_t>(sizeof(value_type)); | |
| } | |
| struct iterator { | |
| StringRef Data; | |
| uint32_t Stride; // size of each element in the list. | |
| const char *Current; | |
| iterator(const ViewArray &A, const char *C) | |
| : Data(A.Data), Stride(A.Stride), Current(C) {} | |
| iterator(const iterator &) = default; | |
| value_type operator*() { | |
| // Explicitly zero the structure so that unused fields are zeroed. It is | |
| // up to the user to know if the fields are used by verifying the PSV | |
| // version. | |
| value_type Val; | |
| std::memset(&Val, 0, sizeof(value_type)); | |
| if (Current >= Data.end()) | |
| return Val; | |
| memcpy(static_cast<void *>(&Val), Current, std::min(Stride, MaxStride())); | |
| if (sys::IsBigEndianHost) | |
| detail::swapBytes(Val); | |
| return Val; | |
| } | |
| iterator operator++() { | |
| if (Current < Data.end()) | |
| Current += Stride; | |
| return *this; | |
| } | |
| iterator operator++(int) { | |
| iterator Tmp = *this; | |
| ++*this; | |
| return Tmp; | |
| } | |
| iterator operator--() { | |
| if (Current > Data.begin()) | |
| Current -= Stride; | |
| return *this; | |
| } | |
| iterator operator--(int) { | |
| iterator Tmp = *this; | |
| --*this; | |
| return Tmp; | |
| } | |
| bool operator==(const iterator I) { return I.Current == Current; } | |
| bool operator!=(const iterator I) { return !(*this == I); } | |
| }; | |
| iterator begin() const { return iterator(*this, Data.begin()); } | |
| iterator end() const { return iterator(*this, Data.end()); } | |
| size_t size() const { return Data.size() / Stride; } | |
| bool isEmpty() const { return Data.empty(); } | |
| }; | |
| namespace DirectX { | |
| class PSVRuntimeInfo { | |
| using ResourceArray = ViewArray<dxbc::PSV::v2::ResourceBindInfo>; | |
| using SigElementArray = ViewArray<dxbc::PSV::v0::SignatureElement>; | |
| StringRef Data; | |
| uint32_t Size; | |
| using InfoStruct = | |
| std::variant<std::monostate, dxbc::PSV::v0::RuntimeInfo, | |
| dxbc::PSV::v1::RuntimeInfo, dxbc::PSV::v2::RuntimeInfo, | |
| dxbc::PSV::v3::RuntimeInfo>; | |
| InfoStruct BasicInfo; | |
| ResourceArray Resources; | |
| StringRef StringTable; | |
| SmallVector<uint32_t> SemanticIndexTable; | |
| SigElementArray SigInputElements; | |
| SigElementArray SigOutputElements; | |
| SigElementArray SigPatchOrPrimElements; | |
| std::array<ViewArray<uint32_t>, 4> OutputVectorMasks; | |
| ViewArray<uint32_t> PatchOrPrimMasks; | |
| std::array<ViewArray<uint32_t>, 4> InputOutputMap; | |
| ViewArray<uint32_t> InputPatchMap; | |
| ViewArray<uint32_t> PatchOutputMap; | |
| public: | |
| PSVRuntimeInfo(StringRef D) : Data(D), Size(0) {} | |
| // Parsing depends on the shader kind | |
| Error parse(uint16_t ShaderKind); | |
| uint32_t getSize() const { return Size; } | |
| uint32_t getResourceCount() const { return Resources.size(); } | |
| ResourceArray getResources() const { return Resources; } | |
| uint32_t getVersion() const { | |
| return Size >= sizeof(dxbc::PSV::v3::RuntimeInfo) | |
| ? 3 | |
| : (Size >= sizeof(dxbc::PSV::v2::RuntimeInfo) ? 2 | |
| : (Size >= sizeof(dxbc::PSV::v1::RuntimeInfo)) ? 1 | |
| : 0); | |
| } | |
| uint32_t getResourceStride() const { return Resources.Stride; } | |
| const InfoStruct &getInfo() const { return BasicInfo; } | |
| template <typename T> const T *getInfoAs() const { | |
| if (const auto *P = std::get_if<dxbc::PSV::v3::RuntimeInfo>(&BasicInfo)) | |
| return static_cast<const T *>(P); | |
| if (std::is_same<T, dxbc::PSV::v3::RuntimeInfo>::value) | |
| return nullptr; | |
| if (const auto *P = std::get_if<dxbc::PSV::v2::RuntimeInfo>(&BasicInfo)) | |
| return static_cast<const T *>(P); | |
| if (std::is_same<T, dxbc::PSV::v2::RuntimeInfo>::value) | |
| return nullptr; | |
| if (const auto *P = std::get_if<dxbc::PSV::v1::RuntimeInfo>(&BasicInfo)) | |
| return static_cast<const T *>(P); | |
| if (std::is_same<T, dxbc::PSV::v1::RuntimeInfo>::value) | |
| return nullptr; | |
| if (const auto *P = std::get_if<dxbc::PSV::v0::RuntimeInfo>(&BasicInfo)) | |
| return static_cast<const T *>(P); | |
| return nullptr; | |
| } | |
| StringRef getStringTable() const { return StringTable; } | |
| ArrayRef<uint32_t> getSemanticIndexTable() const { | |
| return SemanticIndexTable; | |
| } | |
| uint8_t getSigInputCount() const; | |
| uint8_t getSigOutputCount() const; | |
| uint8_t getSigPatchOrPrimCount() const; | |
| SigElementArray getSigInputElements() const { return SigInputElements; } | |
| SigElementArray getSigOutputElements() const { return SigOutputElements; } | |
| SigElementArray getSigPatchOrPrimElements() const { | |
| return SigPatchOrPrimElements; | |
| } | |
| ViewArray<uint32_t> getOutputVectorMasks(size_t Idx) const { | |
| assert(Idx < 4); | |
| return OutputVectorMasks[Idx]; | |
| } | |
| ViewArray<uint32_t> getPatchOrPrimMasks() const { return PatchOrPrimMasks; } | |
| ViewArray<uint32_t> getInputOutputMap(size_t Idx) const { | |
| assert(Idx < 4); | |
| return InputOutputMap[Idx]; | |
| } | |
| ViewArray<uint32_t> getInputPatchMap() const { return InputPatchMap; } | |
| ViewArray<uint32_t> getPatchOutputMap() const { return PatchOutputMap; } | |
| uint32_t getSigElementStride() const { return SigInputElements.Stride; } | |
| bool usesViewID() const { | |
| if (const auto *P = getInfoAs<dxbc::PSV::v1::RuntimeInfo>()) | |
| return P->UsesViewID != 0; | |
| return false; | |
| } | |
| uint8_t getInputVectorCount() const { | |
| if (const auto *P = getInfoAs<dxbc::PSV::v1::RuntimeInfo>()) | |
| return P->SigInputVectors; | |
| return 0; | |
| } | |
| ArrayRef<uint8_t> getOutputVectorCounts() const { | |
| if (const auto *P = getInfoAs<dxbc::PSV::v1::RuntimeInfo>()) | |
| return ArrayRef<uint8_t>(P->SigOutputVectors); | |
| return ArrayRef<uint8_t>(); | |
| } | |
| uint8_t getPatchConstOrPrimVectorCount() const { | |
| if (const auto *P = getInfoAs<dxbc::PSV::v1::RuntimeInfo>()) | |
| return P->GeomData.SigPatchConstOrPrimVectors; | |
| return 0; | |
| } | |
| }; | |
| class Signature { | |
| ViewArray<dxbc::ProgramSignatureElement> Parameters; | |
| uint32_t StringTableOffset; | |
| StringRef StringTable; | |
| public: | |
| ViewArray<dxbc::ProgramSignatureElement>::iterator begin() const { | |
| return Parameters.begin(); | |
| } | |
| ViewArray<dxbc::ProgramSignatureElement>::iterator end() const { | |
| return Parameters.end(); | |
| } | |
| StringRef getName(uint32_t Offset) const { | |
| assert(Offset >= StringTableOffset && | |
| Offset < StringTableOffset + StringTable.size() && | |
| "Offset out of range."); | |
| // Name offsets are from the start of the signature data, not from the start | |
| // of the string table. The header encodes the start offset of the sting | |
| // table, so we convert the offset here. | |
| uint32_t TableOffset = Offset - StringTableOffset; | |
| return StringTable.slice(TableOffset, StringTable.find('\0', TableOffset)); | |
| } | |
| bool isEmpty() const { return Parameters.isEmpty(); } | |
| Error initialize(StringRef Part); | |
| }; | |
| } // namespace DirectX | |
| class DXContainer { | |
| public: | |
| using DXILData = std::pair<dxbc::ProgramHeader, const char *>; | |
| private: | |
| DXContainer(MemoryBufferRef O); | |
| MemoryBufferRef Data; | |
| dxbc::Header Header; | |
| SmallVector<uint32_t, 4> PartOffsets; | |
| std::optional<DXILData> DXIL; | |
| std::optional<uint64_t> ShaderFeatureFlags; | |
| std::optional<dxbc::ShaderHash> Hash; | |
| std::optional<DirectX::PSVRuntimeInfo> PSVInfo; | |
| DirectX::Signature InputSignature; | |
| DirectX::Signature OutputSignature; | |
| DirectX::Signature PatchConstantSignature; | |
| Error parseHeader(); | |
| Error parsePartOffsets(); | |
| Error parseDXILHeader(StringRef Part); | |
| Error parseShaderFeatureFlags(StringRef Part); | |
| Error parseHash(StringRef Part); | |
| Error parsePSVInfo(StringRef Part); | |
| Error parseSignature(StringRef Part, DirectX::Signature &Array); | |
| friend class PartIterator; | |
| public: | |
| // The PartIterator is a wrapper around the iterator for the PartOffsets | |
| // member of the DXContainer. It contains a refernce to the container, and the | |
| // current iterator value, as well as storage for a parsed part header. | |
| class PartIterator { | |
| const DXContainer &Container; | |
| SmallVectorImpl<uint32_t>::const_iterator OffsetIt; | |
| struct PartData { | |
| dxbc::PartHeader Part; | |
| uint32_t Offset; | |
| StringRef Data; | |
| } IteratorState; | |
| friend class DXContainer; | |
| PartIterator(const DXContainer &C, | |
| SmallVectorImpl<uint32_t>::const_iterator It) | |
| : Container(C), OffsetIt(It) { | |
| if (OffsetIt == Container.PartOffsets.end()) | |
| updateIteratorImpl(Container.PartOffsets.back()); | |
| else | |
| updateIterator(); | |
| } | |
| // Updates the iterator's state data. This results in copying the part | |
| // header into the iterator and handling any required byte swapping. This is | |
| // called when incrementing or decrementing the iterator. | |
| void updateIterator() { | |
| if (OffsetIt != Container.PartOffsets.end()) | |
| updateIteratorImpl(*OffsetIt); | |
| } | |
| // Implementation for updating the iterator state based on a specified | |
| // offest. | |
| void updateIteratorImpl(const uint32_t Offset); | |
| public: | |
| PartIterator &operator++() { | |
| if (OffsetIt == Container.PartOffsets.end()) | |
| return *this; | |
| ++OffsetIt; | |
| updateIterator(); | |
| return *this; | |
| } | |
| PartIterator operator++(int) { | |
| PartIterator Tmp = *this; | |
| ++(*this); | |
| return Tmp; | |
| } | |
| bool operator==(const PartIterator &RHS) const { | |
| return OffsetIt == RHS.OffsetIt; | |
| } | |
| bool operator!=(const PartIterator &RHS) const { | |
| return OffsetIt != RHS.OffsetIt; | |
| } | |
| const PartData &operator*() { return IteratorState; } | |
| const PartData *operator->() { return &IteratorState; } | |
| }; | |
| PartIterator begin() const { | |
| return PartIterator(*this, PartOffsets.begin()); | |
| } | |
| PartIterator end() const { return PartIterator(*this, PartOffsets.end()); } | |
| StringRef getData() const { return Data.getBuffer(); } | |
| static Expected<DXContainer> create(MemoryBufferRef Object); | |
| const dxbc::Header &getHeader() const { return Header; } | |
| const std::optional<DXILData> &getDXIL() const { return DXIL; } | |
| std::optional<uint64_t> getShaderFeatureFlags() const { | |
| return ShaderFeatureFlags; | |
| } | |
| std::optional<dxbc::ShaderHash> getShaderHash() const { return Hash; } | |
| const std::optional<DirectX::PSVRuntimeInfo> &getPSVInfo() const { | |
| return PSVInfo; | |
| }; | |
| const DirectX::Signature &getInputSignature() const { return InputSignature; } | |
| const DirectX::Signature &getOutputSignature() const { | |
| return OutputSignature; | |
| } | |
| const DirectX::Signature &getPatchConstantSignature() const { | |
| return PatchConstantSignature; | |
| } | |
| }; | |
| } // namespace object | |
| } // namespace llvm | |
| #endif // LLVM_OBJECT_DXCONTAINER_H |