[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