[llvm] [HLSLSemanticSignatures] Implement the stacked packing of elements (PR #218060)

Finn Plummer via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 11:54:05 PDT 2026


================
@@ -0,0 +1,342 @@
+//===- HLSLSemanticSignaturePackingTest.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/ADT/SmallVector.h"
+#include "llvm/Frontend/HLSL/SemanticSignaturePacking.h"
+#include "llvm/TargetParser/Triple.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <initializer_list>
+
+using namespace llvm;
+using namespace llvm::hlsl;
+
+namespace {
+
+class HLSLSemanticSignaturePackingTest : public testing::Test {
+protected:
+  struct ElementConfig {
+    dxbc::PSV::SemanticKind SemanticKind;
+    uint32_t Rows;
+    uint8_t Cols;
+    dxil::ElementType CompType;
+    dxbc::PSV::InterpolationMode InterpMode;
+  };
+
+  struct ExpectedLocation {
+    uint32_t Row;
+    uint8_t Col;
+  };
+
+  static constexpr ExpectedLocation Unallocated = {UnallocatedRow,
+                                                   UnallocatedCol};
+
+  struct TestConfig {
+    Triple::EnvironmentType ShaderStage;
+    IOType IOTy;
+    SmallVector<ElementConfig> Elements;
+
+    TestConfig(Triple::EnvironmentType ShaderStage, IOType IOTy,
+               std::initializer_list<ElementConfig> Elements)
+        : ShaderStage(ShaderStage), IOTy(IOTy), Elements(Elements) {}
+  };
+
+  SmallVector<SemanticSignatureElement>
+  makeSignature(const TestConfig &Config) {
+    SmallVector<SemanticSignatureElement> Elements;
+    for (const ElementConfig &Element : Config.Elements) {
+      SmallVector<uint32_t> SemanticIndices;
+      for (uint32_t Row = 0; Row != Element.Rows; ++Row)
+        SemanticIndices.push_back(Row);
+
+      Elements.emplace_back(
+          /*SigId=*/static_cast<uint32_t>(Elements.size()),
+          /*SemanticName=*/"TEST",
+          /*CompType=*/Element.CompType,
+          /*SemanticKind=*/Element.SemanticKind,
+          /*SemanticIndices=*/SemanticIndices,
+          /*Cols=*/Element.Cols);
+      Elements.back().InterpMode = Element.InterpMode;
+    }
+    return Elements;
+  }
+
+  Error packStacked(SmallVectorImpl<SemanticSignatureElement> &Elements,
+                    const TestConfig &Config) {
+    return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
+  }
+
+  void expectPacking(const TestConfig &Config, unsigned ExpectedRows,
+                     std::initializer_list<ExpectedLocation> Locations) {
+    SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+    ASSERT_EQ(Elements.size(), Locations.size());
+
+    ASSERT_THAT_ERROR(packStacked(Elements, Config), Succeeded());
+
+    unsigned Rows = 0;
+    for (const SemanticSignatureElement &Element : Elements)
+      if (Element.isAllocated())
+        Rows = std::max(Rows, Element.StartRow + Element.Rows);
+    EXPECT_EQ(Rows, ExpectedRows);
+
+    unsigned Index = 0;
+    for (ExpectedLocation Location : Locations) {
+      EXPECT_EQ(Elements[Index].StartRow, Location.Row) << "element " << Index;
+      EXPECT_EQ(Elements[Index].StartCol, Location.Col) << "element " << Index;
+      ++Index;
+    }
+  }
+
+  void expectPackingError(const TestConfig &Config,
+                          SignaturePackingError::ErrorKind ExpectedKind,
+                          unsigned ExpectedElementIndex) {
+    SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+    Error E = packStacked(Elements, Config);
+    if (!E) {
+      ADD_FAILURE() << "expected a SignaturePackingError";
+      return;
+    }
+    ASSERT_TRUE(E.isA<SignaturePackingError>());
+    handleAllErrors(std::move(E), [&](const SignaturePackingError &PackingErr) {
+      EXPECT_EQ(PackingErr.getErrorKind(), ExpectedKind);
+      EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
+    });
+  }
+};
+
+TEST_F(HLSLSemanticSignaturePackingTest, CreatesSignatureFromConfig) {
+  TestConfig Config(
+      Triple::EnvironmentType::Vertex, IOType::Out,
+      {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/2,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::Position, /*Rows=*/2, /*Cols=*/3,
+        dxil::ElementType::F16, dxbc::PSV::InterpolationMode::Constant}});
+
+  EXPECT_EQ(Config.ShaderStage, Triple::EnvironmentType::Vertex);
+  EXPECT_EQ(Config.IOTy, IOType::Out);
+
+  SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+  ASSERT_EQ(Elements.size(), 2u);
+
+  EXPECT_EQ(Elements[0].SigId, 0u);
+  EXPECT_EQ(Elements[0].SemanticName, "TEST");
+  EXPECT_EQ(Elements[0].CompType, dxil::ElementType::F32);
+  EXPECT_EQ(Elements[0].SemanticKind, dxbc::PSV::SemanticKind::Arbitrary);
+  EXPECT_EQ(Elements[0].SemanticIndices, SmallVector<uint32_t>({0}));
+  EXPECT_EQ(Elements[0].InterpMode, dxbc::PSV::InterpolationMode::Linear);
+  EXPECT_EQ(Elements[0].Rows, 1u);
+  EXPECT_EQ(Elements[0].Cols, 2u);
+  EXPECT_EQ(Elements[0].StartRow, UnallocatedRow);
+  EXPECT_EQ(Elements[0].StartCol, UnallocatedCol);
+  EXPECT_EQ(Elements[0].UsageMask, 0u);
+  EXPECT_EQ(Elements[0].DynIndexMask, 0u);
+  EXPECT_EQ(Elements[0].GSStream, 0u);
+
+  EXPECT_EQ(Elements[1].SigId, 1u);
+  EXPECT_EQ(Elements[1].SemanticKind, dxbc::PSV::SemanticKind::Position);
+  EXPECT_EQ(Elements[1].CompType, dxil::ElementType::F16);
+  EXPECT_EQ(Elements[1].InterpMode, dxbc::PSV::InterpolationMode::Constant);
+  EXPECT_EQ(Elements[1].SemanticIndices, SmallVector<uint32_t>({0, 1}));
+  EXPECT_EQ(Elements[1].Rows, 2u);
+  EXPECT_EQ(Elements[1].Cols, 3u);
+}
+
+//===----------------------------------------------------------------------===//
+// Valid packing tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(HLSLSemanticSignaturePackingTest, SkipsNotAllocatedElements) {
+  // Semantics accessed through dedicated intrinsics do not consume signature
+  // rows and remain unallocated.
+
+  // struct CSIn {
+  //   uint3 DispatchThreadID : SV_DispatchThreadID;
+  //   uint3 GroupID          : SV_GroupID;
+  //   uint GroupIndex        : SV_GroupIndex;
+  // };
+  TestConfig Config(
+      Triple::EnvironmentType::Compute, IOType::In,
+      {{dxbc::PSV::SemanticKind::DispatchThreadID, /*Rows=*/1, /*Cols=*/3,
+        dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined},
+       {dxbc::PSV::SemanticKind::GroupID, /*Rows=*/1, /*Cols=*/3,
+        dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined},
+       {dxbc::PSV::SemanticKind::GroupIndex, /*Rows=*/1, /*Cols=*/1,
+        dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined}});
+
+  // Expected layout: no registers are used.
+  expectPacking(Config, /*ExpectedRows=*/0,
+                {Unallocated, Unallocated, Unallocated});
----------------
inbelic wrote:

Updated the test case in the suggested fashion

https://github.com/llvm/llvm-project/pull/218060


More information about the llvm-commits mailing list