[llvm] [HLSL] Add in-memory representation of Semantic Signatures (PR #209907)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 14:51:27 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-hlsl
Author: Finn Plummer (inbelic)
<details>
<summary>Changes</summary>
Defines the `SemanticSignatureElement` struct in `llvm/Frontend/HLSL/SemanticSignatures` to represent a semantic signature in-memory for use during packing and metadata construction/parsing.
Adds unit testing of the conversion.
Resolves: https://github.com/llvm/llvm-project/issues/204878
Assisted by: Claude Opus 4.8
---
Patch is 32.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209907.diff
5 Files Affected:
- (added) llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h (+101)
- (modified) llvm/lib/Frontend/HLSL/CMakeLists.txt (+1)
- (added) llvm/lib/Frontend/HLSL/SemanticSignatures.cpp (+174)
- (modified) llvm/unittests/Frontend/CMakeLists.txt (+1)
- (added) llvm/unittests/Frontend/HLSLSemanticSignatureMetadataTest.cpp (+494)
``````````diff
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
new file mode 100644
index 0000000000000..7d5efc663e02a
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -0,0 +1,101 @@
+//===- SemanticSignatures.h - HLSL Semantic Signature helper objects ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file contains structure definitions of HLSL Semantic Signature
+/// objects.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_HLSL_SEMANTICSIGNATURES_H
+#define LLVM_FRONTEND_HLSL_SEMANTICSIGNATURES_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/DXContainer.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DXILABI.h"
+#include "llvm/Support/Error.h"
+#include <cstdint>
+#include <string>
+
+namespace llvm {
+
+class LLVMContext;
+class MDNode;
+
+namespace hlsl {
+
+// Definitions of the in-memory data layout structures
+
+// Sentinel values denoting that an element is unallocated
+static constexpr uint32_t UnallocatedRow = ~0U;
+static constexpr uint8_t UnallocatedCol = 0xFF;
+
+// Models a single packed range of signature rows with its semantic name and
+// indices, register placement, component masks, and stage-specific attributes.
+struct SemanticSignatureElement {
+ uint32_t SigId;
+ StringRef SemanticName;
+ dxil::ElementType CompType = dxil::ElementType::Invalid;
+ dxbc::PSV::SemanticKind SemanticKind = dxbc::PSV::SemanticKind::Arbitrary;
+ SmallVector<uint32_t> SemanticIndices;
+ dxbc::PSV::InterpolationMode InterpMode =
+ dxbc::PSV::InterpolationMode::Undefined;
+ uint32_t Rows = 1;
+ uint8_t Cols = 1;
+ uint32_t StartRow = UnallocatedRow;
+ uint8_t StartCol = UnallocatedCol;
+ uint8_t UsageMask = 0;
+ uint8_t DynIndexMask = 0;
+ uint32_t GSStream = 0;
+
+ bool isAllocated() const {
+ return StartRow != UnallocatedRow && StartCol != UnallocatedCol;
+ }
+
+ uint8_t getDeclaredMask() const {
+ if (!isAllocated())
+ return 0;
+ return static_cast<uint8_t>(((1U << Cols) - 1U) << StartCol);
+ }
+
+ uint8_t getAlwaysReadsMask() const { return UsageMask; }
+
+ uint8_t getNeverWritesMask() const {
+ return static_cast<uint8_t>(~UsageMask & getDeclaredMask());
+ }
+
+ dxbc::SigMinPrecision getMinPrecision(bool UseMinPrecision) const {
+ if (!UseMinPrecision)
+ return dxbc::SigMinPrecision::Default;
+ switch (CompType) {
+ case dxil::ElementType::F16:
+ return dxbc::SigMinPrecision::Float16;
+ case dxil::ElementType::I16:
+ case dxil::ElementType::SNormF16:
+ case dxil::ElementType::UNormF16:
+ return dxbc::SigMinPrecision::SInt16;
+ case dxil::ElementType::U16:
+ return dxbc::SigMinPrecision::UInt16;
+ default:
+ return dxbc::SigMinPrecision::Default;
+ }
+ }
+
+ // Parse a signature element from its metadata representation
+ LLVM_ABI static Expected<SemanticSignatureElement>
+ fromMetadata(const MDNode *Node);
+
+ // Build the metadata representation of this signature element
+ LLVM_ABI MDNode *toMetadata(LLVMContext &Ctx) const;
+};
+
+} // namespace hlsl
+} // namespace llvm
+
+#endif // LLVM_FRONTEND_HLSL_SEMANTICSIGNATURES_H
diff --git a/llvm/lib/Frontend/HLSL/CMakeLists.txt b/llvm/lib/Frontend/HLSL/CMakeLists.txt
index 3d225770e8d5b..b8d1456a787ce 100644
--- a/llvm/lib/Frontend/HLSL/CMakeLists.txt
+++ b/llvm/lib/Frontend/HLSL/CMakeLists.txt
@@ -5,6 +5,7 @@ add_llvm_component_library(LLVMFrontendHLSL
HLSLRootSignature.cpp
RootSignatureMetadata.cpp
RootSignatureValidations.cpp
+ SemanticSignatures.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
new file mode 100644
index 0000000000000..8032f5975ae6a
--- /dev/null
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -0,0 +1,174 @@
+//===- SemanticSignatures.cpp - HLSL Semantic Signature helpers -----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements a library for working with HLSL shader input and
+/// output semantic signatures and their DirectX metadata representation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/HLSL/SemanticSignatures.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+using namespace llvm::hlsl;
+
+namespace {
+// The fixed number of operands in a signature element node
+constexpr unsigned NumElementOperands = 13;
+
+// Inclusive upper bounds of the operand enums
+constexpr uint32_t MaxCompType =
+ static_cast<uint32_t>(dxil::ElementType::PackedU8x32);
+constexpr uint32_t MaxSemanticKind =
+ static_cast<uint32_t>(dxbc::PSV::SemanticKind::Invalid);
+constexpr uint32_t MaxInterpMode =
+ static_cast<uint32_t>(dxbc::PSV::InterpolationMode::Invalid);
+
+Error makeError(const Twine &Msg) {
+ return createStringError(inconvertibleErrorCode(), Msg);
+}
+
+Expected<uint64_t> extractInt(const MDNode *Node, unsigned OpId) {
+ auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Node->getOperand(OpId));
+ if (!CI)
+ return makeError("expected integer operand " + Twine(OpId));
+ return CI->getZExtValue();
+}
+} // namespace
+
+Expected<SemanticSignatureElement>
+SemanticSignatureElement::fromMetadata(const MDNode *Node) {
+ if (!Node)
+ return makeError("signature element node is null");
+ if (Node->getNumOperands() != NumElementOperands)
+ return makeError("signature element node has wrong number of operands");
+
+ SemanticSignatureElement Elem;
+
+ Expected<uint64_t> SigId = extractInt(Node, 0);
+ if (!SigId)
+ return SigId.takeError();
+ Elem.SigId = *SigId;
+
+ auto *Name = dyn_cast<MDString>(Node->getOperand(1));
+ if (!Name)
+ return makeError("expected semantic name string");
+ Elem.SemanticName = Name->getString();
+
+ Expected<uint64_t> CompType = extractInt(Node, 2);
+ if (!CompType)
+ return CompType.takeError();
+ if (*CompType > MaxCompType)
+ return makeError("invalid component type");
+ Elem.CompType = static_cast<dxil::ElementType>(*CompType);
+
+ Expected<uint64_t> SemanticKind = extractInt(Node, 3);
+ if (!SemanticKind)
+ return SemanticKind.takeError();
+ if (*SemanticKind > MaxSemanticKind)
+ return makeError("invalid semantic kind");
+ Elem.SemanticKind = static_cast<dxbc::PSV::SemanticKind>(*SemanticKind);
+
+ auto *Indices = dyn_cast<MDNode>(Node->getOperand(4));
+ if (!Indices)
+ return makeError("expected semantic indices node");
+ for (unsigned I = 0, E = Indices->getNumOperands(); I != E; ++I) {
+ Expected<uint64_t> Index = extractInt(Indices, I);
+ if (!Index)
+ return Index.takeError();
+ Elem.SemanticIndices.push_back(*Index);
+ }
+
+ Expected<uint64_t> InterpMode = extractInt(Node, 5);
+ if (!InterpMode)
+ return InterpMode.takeError();
+ if (*InterpMode > MaxInterpMode)
+ return makeError("invalid interpolation mode");
+ Elem.InterpMode = static_cast<dxbc::PSV::InterpolationMode>(*InterpMode);
+
+ Expected<uint64_t> Rows = extractInt(Node, 6);
+ if (!Rows)
+ return Rows.takeError();
+ Elem.Rows = *Rows;
+
+ Expected<uint64_t> Cols = extractInt(Node, 7);
+ if (!Cols)
+ return Cols.takeError();
+ if (*Cols < 1 || *Cols > 4)
+ return makeError("number of components per row must be within 1-4");
+ Elem.Cols = *Cols;
+
+ Expected<uint64_t> StartRow = extractInt(Node, 8);
+ if (!StartRow)
+ return StartRow.takeError();
+ Elem.StartRow = *StartRow;
+
+ Expected<uint64_t> StartCol = extractInt(Node, 9);
+ if (!StartCol)
+ return StartCol.takeError();
+ if (*StartCol > 3 && *StartCol != UnallocatedCol)
+ return makeError("start column must be within 0-3 or unallocated");
+ Elem.StartCol = *StartCol;
+
+ // The row/col sentinels are always set together
+ if ((Elem.StartRow == UnallocatedRow) != (Elem.StartCol == UnallocatedCol))
+ return makeError("start row and column sentinels must be set together");
+
+ Expected<uint64_t> UsageMask = extractInt(Node, 10);
+ if (!UsageMask)
+ return UsageMask.takeError();
+ if (*UsageMask > 0xF)
+ return makeError("usage mask must be a 4-bit value");
+ Elem.UsageMask = *UsageMask;
+
+ Expected<uint64_t> DynIndexMask = extractInt(Node, 11);
+ if (!DynIndexMask)
+ return DynIndexMask.takeError();
+ if (*DynIndexMask > 0xF)
+ return makeError("dynamic index mask must be a 4-bit value");
+ Elem.DynIndexMask = *DynIndexMask;
+
+ Expected<uint64_t> GSStream = extractInt(Node, 12);
+ if (!GSStream)
+ return GSStream.takeError();
+ if (*GSStream > 3)
+ return makeError("geometry shader stream index must be within 0-3");
+ Elem.GSStream = *GSStream;
+
+ if (Elem.SemanticIndices.size() != Elem.Rows)
+ return makeError("number of semantic indices must equal the number of rows");
+
+ return Elem;
+}
+
+MDNode *SemanticSignatureElement::toMetadata(LLVMContext &Ctx) const {
+ Type *I32Ty = Type::getInt32Ty(Ctx);
+ Type *I8Ty = Type::getInt8Ty(Ctx);
+ auto GetI32 = [&](uint32_t Val) -> Metadata * {
+ return ConstantAsMetadata::get(ConstantInt::get(I32Ty, Val));
+ };
+ auto GetI8 = [&](uint8_t Val) -> Metadata * {
+ return ConstantAsMetadata::get(ConstantInt::get(I8Ty, Val));
+ };
+
+ SmallVector<Metadata *> IndexOps;
+ for (uint32_t Index : SemanticIndices)
+ IndexOps.push_back(GetI32(Index));
+
+ return MDNode::get(
+ Ctx, {GetI32(SigId), MDString::get(Ctx, SemanticName),
+ GetI32(static_cast<uint32_t>(CompType)),
+ GetI32(static_cast<uint32_t>(SemanticKind)),
+ MDNode::get(Ctx, IndexOps),
+ GetI32(static_cast<uint32_t>(InterpMode)), GetI32(Rows),
+ GetI8(Cols), GetI32(StartRow), GetI8(StartCol), GetI8(UsageMask),
+ GetI8(DynIndexMask), GetI32(GSStream)});
+}
diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index 1ce34e77cb348..ff3b382a67fe9 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -15,6 +15,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(LLVMFrontendTests
HLSLBindingTest.cpp
HLSLRootSignatureDumpTest.cpp
+ HLSLSemanticSignatureMetadataTest.cpp
OpenACCTest.cpp
OpenMPContextTest.cpp
OpenMPIRBuilderTest.cpp
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignatureMetadataTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignatureMetadataTest.cpp
new file mode 100644
index 0000000000000..8950498887e2f
--- /dev/null
+++ b/llvm/unittests/Frontend/HLSLSemanticSignatureMetadataTest.cpp
@@ -0,0 +1,494 @@
+//===- HLSLSemanticSignatureMetadataTest.cpp ------------------------------===//
+//
+// 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 "llvm/Frontend/HLSL/SemanticSignatures.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::hlsl;
+
+namespace {
+
+class HLSLSemanticSignatureMetadataTest : public testing::Test {
+protected:
+ LLVMContext Ctx;
+
+ Metadata *getI32(uint32_t Val) {
+ return ConstantAsMetadata::get(
+ ConstantInt::get(Type::getInt32Ty(Ctx), Val));
+ }
+
+ Metadata *getI8(uint8_t Val) {
+ return ConstantAsMetadata::get(
+ ConstantInt::get(Type::getInt8Ty(Ctx), Val));
+ }
+
+ Metadata *getStr(StringRef Val) { return MDString::get(Ctx, Val); }
+
+ MDNode *getIndices(ArrayRef<uint32_t> Indices) {
+ SmallVector<Metadata *> Ops;
+ for (uint32_t I : Indices)
+ Ops.push_back(getI32(I));
+ return MDNode::get(Ctx, Ops);
+ }
+
+ // Assemble a raw signature element node from the example in the spec
+ MDNode *getElement(uint32_t SigId, StringRef Name, uint32_t CompType,
+ uint32_t SemanticKind, ArrayRef<uint32_t> Indices,
+ uint32_t InterpMode, uint32_t Rows, uint8_t Cols,
+ uint32_t StartRow, uint8_t StartCol, uint8_t UsageMask,
+ uint8_t DynIndexMask, uint32_t GSStream) {
+ return MDNode::get(
+ Ctx, {getI32(SigId), getStr(Name), getI32(CompType),
+ getI32(SemanticKind), getIndices(Indices), getI32(InterpMode),
+ getI32(Rows), getI8(Cols), getI32(StartRow), getI8(StartCol),
+ getI8(UsageMask), getI8(DynIndexMask), getI32(GSStream)});
+ }
+
+ // Read back an integer operand
+ uint64_t getIntOp(const MDNode *N, unsigned I) {
+ return mdconst::extract<ConstantInt>(N->getOperand(I))->getZExtValue();
+ }
+
+ // Read back a string operand
+ StringRef getStrOp(const MDNode *N, unsigned I) {
+ return cast<MDString>(N->getOperand(I))->getString();
+ }
+};
+
+//===----------------------------------------------------------------------===//
+// Success cases
+//===----------------------------------------------------------------------===//
+
+TEST_F(HLSLSemanticSignatureMetadataTest, StructHelpers) {
+ SemanticSignatureElement Elem;
+ EXPECT_FALSE(Elem.isAllocated());
+
+ Elem.Cols = 4;
+ Elem.StartRow = 0;
+ Elem.StartCol = 0;
+ EXPECT_TRUE(Elem.isAllocated());
+ EXPECT_EQ(Elem.getDeclaredMask(), 0xF);
+}
+
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElement) {
+ MDNode *Node = getElement(/*SigId=*/1, "TEXCOORD", /*CompType=*/9,
+ /*SemanticKind=*/0, /*Indices=*/{0, 1},
+ /*InterpMode=*/0, /*Rows=*/2, /*Cols=*/4,
+ /*StartRow=*/1, /*StartCol=*/0, /*UsageMask=*/0,
+ /*DynIndexMask=*/0, /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+
+ EXPECT_EQ(Elem->SigId, 1u);
+ EXPECT_EQ(Elem->SemanticName, "TEXCOORD");
+ EXPECT_EQ(Elem->CompType, dxil::ElementType::F32);
+ EXPECT_EQ(Elem->SemanticKind, dxbc::PSV::SemanticKind::Arbitrary);
+ EXPECT_THAT(Elem->SemanticIndices, testing::ElementsAre(0u, 1u));
+ EXPECT_EQ(Elem->InterpMode, dxbc::PSV::InterpolationMode::Undefined);
+ EXPECT_EQ(Elem->Rows, 2u);
+ EXPECT_EQ(Elem->Cols, 4u);
+ EXPECT_EQ(Elem->StartRow, 1u);
+ EXPECT_EQ(Elem->StartCol, 0u);
+ EXPECT_EQ(Elem->UsageMask, 0u);
+ EXPECT_EQ(Elem->DynIndexMask, 0u);
+ EXPECT_EQ(Elem->GSStream, 0u);
+}
+
+// SV_Target output with a non-zero usage/dynamic-index mask and semantic index
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementSystemValue) {
+ MDNode *Node = getElement(/*SigId=*/1, "SV_Target", /*CompType=*/9,
+ /*SemanticKind=*/16, /*Indices=*/{1},
+ /*InterpMode=*/0, /*Rows=*/1, /*Cols=*/4,
+ /*StartRow=*/1, /*StartCol=*/0, /*UsageMask=*/0x7,
+ /*DynIndexMask=*/0x1, /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+
+ EXPECT_EQ(Elem->SemanticName, "SV_Target");
+ EXPECT_EQ(Elem->SemanticKind, dxbc::PSV::SemanticKind::Target);
+ EXPECT_THAT(Elem->SemanticIndices, testing::ElementsAre(1u));
+ EXPECT_EQ(Elem->UsageMask, 0x7u);
+ EXPECT_EQ(Elem->DynIndexMask, 0x1u);
+}
+
+// An unallocated element uses the row/col sentinels
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementUnallocated) {
+ MDNode *Node = getElement(/*SigId=*/0, "POSITION", /*CompType=*/9,
+ /*SemanticKind=*/0, /*Indices=*/{0},
+ /*InterpMode=*/0, /*Rows=*/1, /*Cols=*/4,
+ /*StartRow=*/UnallocatedRow, /*StartCol=*/UnallocatedCol,
+ /*UsageMask=*/0, /*DynIndexMask=*/0, /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+
+ EXPECT_EQ(Elem->StartRow, UnallocatedRow);
+ EXPECT_EQ(Elem->StartCol, UnallocatedCol);
+ EXPECT_FALSE(Elem->isAllocated());
+}
+
+// Every component type value maps onto the matching dxil::ElementType
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementComponentTypes) {
+ for (dxil::ElementType CompType :
+ {dxil::ElementType::I32, dxil::ElementType::U32,
+ dxil::ElementType::F16, dxil::ElementType::F32,
+ dxil::ElementType::F64, dxil::ElementType::I16}) {
+ MDNode *Node = getElement(
+ /*SigId=*/0, "A", static_cast<uint32_t>(CompType), /*SemanticKind=*/0,
+ /*Indices=*/{0}, /*InterpMode=*/0, /*Rows=*/1, /*Cols=*/1,
+ /*StartRow=*/0, /*StartCol=*/0, /*UsageMask=*/0, /*DynIndexMask=*/0,
+ /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+ EXPECT_EQ(Elem->CompType, CompType);
+ }
+}
+
+// Every interpolation mode value maps onto the matching enumerator
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementInterpModes) {
+ for (dxbc::PSV::InterpolationMode Mode :
+ {dxbc::PSV::InterpolationMode::Constant,
+ dxbc::PSV::InterpolationMode::Linear,
+ dxbc::PSV::InterpolationMode::LinearCentroid,
+ dxbc::PSV::InterpolationMode::LinearNoperspective,
+ dxbc::PSV::InterpolationMode::LinearSample}) {
+ MDNode *Node = getElement(
+ /*SigId=*/0, "A", /*CompType=*/9, /*SemanticKind=*/0, /*Indices=*/{0},
+ static_cast<uint32_t>(Mode), /*Rows=*/1, /*Cols=*/1, /*StartRow=*/0,
+ /*StartCol=*/0, /*UsageMask=*/0, /*DynIndexMask=*/0, /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+ EXPECT_EQ(Elem->InterpMode, Mode);
+ }
+}
+
+// A column-offset allocation drives the derived declared/usage masks
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementDerivedMasks) {
+ MDNode *Node = getElement(/*SigId=*/0, "SV_Position", /*CompType=*/9,
+ /*SemanticKind=*/3, /*Indices=*/{0},
+ /*InterpMode=*/4, /*Rows=*/1, /*Cols=*/2,
+ /*StartRow=*/0, /*StartCol=*/1, /*UsageMask=*/0x2,
+ /*DynIndexMask=*/0, /*GSStream=*/0);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+
+ EXPECT_EQ(Elem->SemanticKind, dxbc::PSV::SemanticKind::Position);
+ EXPECT_TRUE(Elem->isAllocated());
+ // ((1 << 2) - 1) << 1 == 0b0110
+ EXPECT_EQ(Elem->getDeclaredMask(), 0x6);
+ EXPECT_EQ(Elem->getAlwaysReadsMask(), 0x2);
+ // ~0x2 & 0x6 == 0x4
+ EXPECT_EQ(Elem->getNeverWritesMask(), 0x4);
+}
+
+// A geometry shader output carries a non-zero stream index
+TEST_F(HLSLSemanticSignatureMetadataTest, MetadataToElementGSStream) {
+ MDNode *Node = getElement(/*SigId=*/0, "A", /*CompType=*/9,
+ /*SemanticKind=*/0, /*Indices=*/{0},
+ /*InterpMode=*/0, /*Rows=*/1, /*Cols=*/1,
+ /*StartRow=*/0, /*StartCol=*/0, /*UsageMask=*/0,
+ /*DynIndexMask=*/0, /*GSStream=*/3);
+
+ Expected<SemanticSignatureElement> Elem =
+ SemanticSignatureElement::fromMetadata(Node);
+ ASSERT_THAT_EXPECTED(Elem, Succeeded());
+ EXPECT_EQ(Ele...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/209907
More information about the llvm-commits
mailing list