[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:53:45 PDT 2026
https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/218060
>From 02d7a21714246a506979f1a2787203407f593808 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 19:22:52 +0000
Subject: [PATCH 01/17] add test harness and snub
---
.../Frontend/HLSL/SemanticSignaturePacking.h | 42 ++++++
llvm/lib/Frontend/HLSL/CMakeLists.txt | 1 +
.../HLSL/SemanticSignaturePacking.cpp | 22 +++
llvm/unittests/Frontend/CMakeLists.txt | 1 +
.../HLSLSemanticSignaturePackingTest.cpp | 137 ++++++++++++++++++
5 files changed, 203 insertions(+)
create mode 100644 llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
create mode 100644 llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
create mode 100644 llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
new file mode 100644
index 0000000000000..8f01d07fa0c0e
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -0,0 +1,42 @@
+//===- SemanticSignaturePacking.h - HLSL signature packing 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 declares helpers for packing HLSL semantic signatures.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_HLSL_SEMANTICSIGNATUREPACKING_H
+#define LLVM_FRONTEND_HLSL_SEMANTICSIGNATUREPACKING_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Frontend/HLSL/SemanticSignatures.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm::hlsl {
+
+/// Iterates through Elements that belong to the signature described by
+/// ShaderStage and IOTy and packs each element into 32 registers with 4
+/// components by updating its StartRow and StartCol in place. An element is
+/// left unallocated if it is not part of the signature.
+///
+/// Elements are visited in declaration order. Each element starts at column
+/// zero of the first row after the preceding element, and a multi-row element
+/// occupies consecutive rows. Elements are never co-packed into the same row;
+/// interpolation mode, component type, and semantic kind do not otherwise
+/// affect placement.
+///
+/// Returns an error if all eligible elements cannot be placed.
+LLVM_ABI Error
+packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
+ Triple::EnvironmentType ShaderStage, IOType IOTy);
+
+} // namespace llvm::hlsl
+
+#endif // LLVM_FRONTEND_HLSL_SEMANTICSIGNATUREPACKING_H
diff --git a/llvm/lib/Frontend/HLSL/CMakeLists.txt b/llvm/lib/Frontend/HLSL/CMakeLists.txt
index b8d1456a787ce..4703458c68168 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
+ SemanticSignaturePacking.cpp
SemanticSignatures.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
new file mode 100644
index 0000000000000..17b17a91ac2ea
--- /dev/null
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -0,0 +1,22 @@
+//===- SemanticSignaturePacking.cpp - HLSL signature packing 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 helpers for packing HLSL semantic signatures.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/HLSL/SemanticSignaturePacking.h"
+
+using namespace llvm;
+using namespace llvm::hlsl;
+
+Error llvm::hlsl::packSignatureStacked(
+ MutableArrayRef<SemanticSignatureElement>, Triple::EnvironmentType,
+ IOType) {
+ return Error::success();
+}
diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index b8a26561069d5..2373ae29e0599 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -18,6 +18,7 @@ add_llvm_unittest(LLVMFrontendTests
HLSLRootSignatureDumpTest.cpp
HLSLSemanticStagesTest.cpp
HLSLSemanticSignatureMetadataTest.cpp
+ HLSLSemanticSignaturePackingTest.cpp
OpenACCTest.cpp
OpenMPContextTest.cpp
OpenMPIRBuilderTest.cpp
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
new file mode 100644
index 0000000000000..218f891025524
--- /dev/null
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -0,0 +1,137 @@
+//===- 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;
+ };
+
+ 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, StringRef Message) {
+ SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+ EXPECT_THAT_ERROR(packStacked(Elements, Config),
+ FailedWithMessage(Message));
+ }
+};
+
+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);
+}
+
+} // namespace
>From 012a752a7685e14e217a397f1cd201882ef899f1 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 19:26:01 +0000
Subject: [PATCH 02/17] add error kind
---
.../Frontend/HLSL/SemanticSignaturePacking.h | 29 ++++++++++++++++++-
.../HLSL/SemanticSignaturePacking.cpp | 11 +++++++
.../HLSLSemanticSignaturePackingTest.cpp | 12 ++++++--
3 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 8f01d07fa0c0e..311fe6abef742 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -21,6 +21,32 @@
namespace llvm::hlsl {
+/// Denotes the element that could not be packed and why.
+class SignaturePackingError : public ErrorInfo<SignaturePackingError> {
+public:
+ enum ErrorKind {
+ SignatureOverflow,
+ };
+
+ LLVM_ABI static char ID;
+
+ SignaturePackingError(ErrorKind Kind, unsigned ElementIndex)
+ : Kind(Kind), ElementIndex(ElementIndex) {}
+
+ ErrorKind getErrorKind() const { return Kind; }
+ unsigned getElementIndex() const { return ElementIndex; }
+
+ LLVM_ABI void log(raw_ostream &OS) const override;
+
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+
+private:
+ ErrorKind Kind;
+ unsigned ElementIndex;
+};
+
/// Iterates through Elements that belong to the signature described by
/// ShaderStage and IOTy and packs each element into 32 registers with 4
/// components by updating its StartRow and StartCol in place. An element is
@@ -32,7 +58,8 @@ namespace llvm::hlsl {
/// interpolation mode, component type, and semantic kind do not otherwise
/// affect placement.
///
-/// Returns an error if all eligible elements cannot be placed.
+/// Returns a SignaturePackingError that denotes the first element that cannot
+/// be placed, or success if all eligible elements were placed.
LLVM_ABI Error
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 17b17a91ac2ea..75a8f2a0e9700 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -15,6 +15,17 @@
using namespace llvm;
using namespace llvm::hlsl;
+char SignaturePackingError::ID;
+
+void SignaturePackingError::log(raw_ostream &OS) const {
+ switch (Kind) {
+ case SignatureOverflow:
+ OS << "signature elements do not fit in 32 rows";
+ break;
+ }
+ OS << " (element " << ElementIndex << ")";
+}
+
Error llvm::hlsl::packSignatureStacked(
MutableArrayRef<SemanticSignatureElement>, Triple::EnvironmentType,
IOType) {
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index 218f891025524..8c3b0aa231650 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -90,10 +90,16 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
}
}
- void expectPackingError(const TestConfig &Config, StringRef Message) {
+ void expectPackingError(const TestConfig &Config,
+ SignaturePackingError::ErrorKind ExpectedKind,
+ unsigned ExpectedElementIndex) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
- EXPECT_THAT_ERROR(packStacked(Elements, Config),
- FailedWithMessage(Message));
+ Error E = packStacked(Elements, Config);
+ ASSERT_TRUE(E.isA<SignaturePackingError>());
+ handleAllErrors(std::move(E), [&](const SignaturePackingError &PackingErr) {
+ EXPECT_EQ(PackingErr.getErrorKind(), ExpectedKind);
+ EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
+ });
}
};
>From 4372ac3707e66f6f8593bae8fbf12b82eb65081b Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 19:57:20 +0000
Subject: [PATCH 03/17] add test cases
---
.../HLSLSemanticSignaturePackingTest.cpp | 199 ++++++++++++++++++
1 file changed, 199 insertions(+)
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index 8c3b0aa231650..a1fd495638613 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -34,6 +34,9 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
uint8_t Col;
};
+ static constexpr ExpectedLocation Unallocated = {UnallocatedRow,
+ UnallocatedCol};
+
struct TestConfig {
Triple::EnvironmentType ShaderStage;
IOType IOTy;
@@ -95,6 +98,10 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
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);
@@ -140,4 +147,196 @@ TEST_F(HLSLSemanticSignaturePackingTest, CreatesSignatureFromConfig) {
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});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, StacksInDeclarationOrder) {
+ // Elements are assigned whole rows in declaration order, regardless of their
+ // semantic interpretation.
+
+ // struct VSIn {
+ // uint VertexID : SV_VertexID;
+ // float2 Data : DATA;
+ // float3 ClipDistance : SV_ClipDistance;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::VertexID, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Constant},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/2,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::ClipDistance, /*Rows=*/1, /*Cols=*/3,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+
+ // Expected layout:
+ // reg0: VertexID.x | unused.yzw
+ // reg1: Data.xy | unused.zw
+ // reg2: ClipDistance.xyz | unused.w
+ expectPacking(
+ Config, /*ExpectedRows=*/3,
+ {{/*Row=*/0, /*Col=*/0}, {/*Row=*/1, /*Col=*/0}, {/*Row=*/2, /*Col=*/0}});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, DoesNotCoPackElements) {
+ // Elements are never co-packed even when they would fit in one row.
+
+ // struct VSIn {
+ // float A : A;
+ // float B : B;
+ // float C : C;
+ // float D : D;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+
+ // Expected layout:
+ // reg0: A.x | unused.yzw
+ // reg1: B.x | unused.yzw
+ // reg2: C.x | unused.yzw
+ // reg3: D.x | unused.yzw
+ expectPacking(Config, /*ExpectedRows=*/4,
+ {{/*Row=*/0, /*Col=*/0},
+ {/*Row=*/1, /*Col=*/0},
+ {/*Row=*/2, /*Col=*/0},
+ {/*Row=*/3, /*Col=*/0}});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, StacksMultiRowElements) {
+ // A multi-row element occupies consecutive whole rows.
+
+ // struct VSIn {
+ // float A[3] : A;
+ // float3 B[2] : B;
+ // float4 C : C;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/3, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/2, /*Cols=*/3,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/4,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+
+ // Expected layout:
+ // reg0: A[0].x | unused.yzw
+ // reg1: A[1].x | unused.yzw
+ // reg2: A[2].x | unused.yzw
+ // reg3: B[0].xyz | unused.w
+ // reg4: B[1].xyz | unused.w
+ // reg5: C.xyzw
+ expectPacking(
+ Config, /*ExpectedRows=*/6,
+ {{/*Row=*/0, /*Col=*/0}, {/*Row=*/3, /*Col=*/0}, {/*Row=*/5, /*Col=*/0}});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, ExactlyFillsSignature) {
+ // An element may occupy all available signature rows.
+
+ // struct VSIn {
+ // float4 A[32] : A;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/MaxSignatureRows,
+ /*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
+ dxbc::PSV::InterpolationMode::Linear}});
+
+ // Expected layout:
+ // reg0-31: A[0-31].xyzw
+ expectPacking(Config, /*ExpectedRows=*/MaxSignatureRows,
+ {{/*Row=*/0, /*Col=*/0}});
+}
+
+//===----------------------------------------------------------------------===//
+// Packing error tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(HLSLSemanticSignaturePackingTest, RejectsSignatureOverflow) {
+ // A signature that requires more than 32 rows cannot be packed.
+
+ // struct VSIn {
+ // float4 A0 : A0;
+ // ...
+ // float4 A32 : A32;
+ // };
+ TestConfig Config(Triple::EnvironmentType::Vertex, IOType::In, {});
+ for (unsigned I = 0; I != MaxSignatureRows + 1; ++I)
+ Config.Elements.push_back({dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1,
+ /*Cols=*/MaxSignatureCols,
+ dxil::ElementType::F32,
+ dxbc::PSV::InterpolationMode::Linear});
+
+ // The last element is the one that no longer fits.
+ expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ /*ExpectedElementIndex=*/MaxSignatureRows);
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, RejectsSingleElementOverflow) {
+ // A single element may also require more rows than the signature provides.
+
+ // struct VSIn {
+ // float4 A[33] : A;
+ // };
+ TestConfig Config(Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary,
+ /*Rows=*/MaxSignatureRows + 1,
+ /*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
+ dxbc::PSV::InterpolationMode::Linear}});
+
+ expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ /*ExpectedElementIndex=*/0);
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, RejectsMultiRowSignatureOverflow) {
+ // Each element is valid on its own, but together they require 33 rows.
+
+ // struct VSIn {
+ // float4 A[31] : A;
+ // float4 B[2] : B;
+ // };
+ TestConfig Config(Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/31,
+ /*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
+ dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/2,
+ /*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
+ dxbc::PSV::InterpolationMode::Linear}});
+
+ expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ /*ExpectedElementIndex=*/1);
+}
+
} // namespace
>From d5f9b864e8b7c4c0fc67e5d86c7756379bb2ecb8 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:09:34 +0000
Subject: [PATCH 04/17] implement stacked sort
---
.../Frontend/HLSL/SemanticSignaturePacking.h | 3 ++
.../HLSL/SemanticSignaturePacking.cpp | 34 +++++++++++++++++--
2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 311fe6abef742..354de1d83dc73 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -21,6 +21,9 @@
namespace llvm::hlsl {
+static constexpr unsigned MaxSignatureRows = 32;
+static constexpr unsigned MaxSignatureCols = 4;
+
/// Denotes the element that could not be packed and why.
class SignaturePackingError : public ErrorInfo<SignaturePackingError> {
public:
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 75a8f2a0e9700..59289497dce2b 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//
#include "llvm/Frontend/HLSL/SemanticSignaturePacking.h"
+#include "llvm/ADT/STLExtras.h"
+#include <cassert>
using namespace llvm;
using namespace llvm::hlsl;
@@ -27,7 +29,35 @@ void SignaturePackingError::log(raw_ostream &OS) const {
}
Error llvm::hlsl::packSignatureStacked(
- MutableArrayRef<SemanticSignatureElement>, Triple::EnvironmentType,
- IOType) {
+ MutableArrayRef<SemanticSignatureElement> Elements,
+ Triple::EnvironmentType ShaderStage, IOType IOTy) {
+ unsigned NextRow = 0;
+ for (const auto &[Index, Element] : enumerate(Elements)) {
+ assert(Element.StartRow == UnallocatedRow &&
+ Element.StartCol == UnallocatedCol && "already allocated?");
+ assert(Element.Rows > 0 && "signature element must have at least one row");
+ assert(Element.Cols > 0 && Element.Cols <= MaxSignatureCols &&
+ "signature element must have between 1 and 4 columns");
+
+ SemanticInterpretation Interpretation =
+ getInterpretationKind(Element.SemanticKind, ShaderStage, IOTy);
+ if (Interpretation == SemanticInterpretation::NotAllocated)
+ continue;
+
+ assert((Interpretation == SemanticInterpretation::Arbitrary ||
+ Interpretation == SemanticInterpretation::SV ||
+ Interpretation == SemanticInterpretation::SGV) &&
+ "unexpected semantic interpretation for stacked packing");
+
+ if (Element.Rows > MaxSignatureRows - NextRow)
+ return make_error<SignaturePackingError>(
+ SignaturePackingError::SignatureOverflow,
+ static_cast<unsigned>(Index));
+
+ Element.StartRow = NextRow;
+ Element.StartCol = 0;
+ NextRow += Element.Rows;
+ }
+
return Error::success();
}
>From 11257007c625c4f6d8076638623601d19830ba29 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:20:15 +0000
Subject: [PATCH 05/17] document stacked signature packing
---
llvm/docs/DirectX/SemanticSignatures.md | 48 +++++++++++++++++++
.../Frontend/HLSL/SemanticSignaturePacking.h | 14 +-----
2 files changed, 50 insertions(+), 12 deletions(-)
diff --git a/llvm/docs/DirectX/SemanticSignatures.md b/llvm/docs/DirectX/SemanticSignatures.md
index 6860d9519221a..985a6d596a9e5 100644
--- a/llvm/docs/DirectX/SemanticSignatures.md
+++ b/llvm/docs/DirectX/SemanticSignatures.md
@@ -125,3 +125,51 @@ The following container fields are derived from the operands above:
A metadata node of one or more semantic indices. Its length must equal the
`Rows` field of the containing signature element.
+
+## Signature Packing
+
+Before a semantic signature is serialized, each element that participates in
+packing is assigned a location in a fixed register space of 32 rows and 4
+columns. An element occupies a rectangle of `Rows` consecutive registers and
+`Cols` consecutive components. Its allocated location is recorded in
+`StartRow` and `StartCol`.
+
+The packing helper classifies each element from its semantic kind, shader stage,
+and I/O type. Elements with the `NotAllocated` interpretation are accessed by
+other means and retain the unallocated row and column sentinels. The remaining
+interpretations accepted by a packing algorithm are assigned locations
+according to that algorithm's rules. If an eligible element cannot be placed,
+packing returns a `SignaturePackingError` identifying the element that failed.
+
+The packing APIs and their in-memory element representation are declared in
+[SemanticSignaturePacking.h].
+
+[SemanticSignaturePacking.h]: https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+
+### Stacked Packing
+
+Stacked packing is used for a vertex shader input signature. Eligible elements
+are visited in declaration order. Each starts at column zero of the first row
+after the preceding element, and a multi-row element occupies consecutive rows.
+Elements are never co-packed into the unused columns of another element, and
+interpolation mode, component type, and semantic interpretation do not otherwise
+affect placement.
+
+For example:
+
+```hlsl
+struct VSIn {
+ float A : A;
+ float3 B[2] : B;
+ uint VertexID : SV_VertexID;
+};
+```
+
+The signature is allocated as:
+
+```text
+reg0: A.x | unused.yzw
+reg1: B[0].xyz | unused.w
+reg2: B[1].xyz | unused.w
+reg3: VertexID.x | unused.yzw
+```
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 354de1d83dc73..14cd477994d68 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -50,19 +50,9 @@ class SignaturePackingError : public ErrorInfo<SignaturePackingError> {
unsigned ElementIndex;
};
-/// Iterates through Elements that belong to the signature described by
-/// ShaderStage and IOTy and packs each element into 32 registers with 4
-/// components by updating its StartRow and StartCol in place. An element is
-/// left unallocated if it is not part of the signature.
+/// Packs eligible signature elements into consecutive rows.
///
-/// Elements are visited in declaration order. Each element starts at column
-/// zero of the first row after the preceding element, and a multi-row element
-/// occupies consecutive rows. Elements are never co-packed into the same row;
-/// interpolation mode, component type, and semantic kind do not otherwise
-/// affect placement.
-///
-/// Returns a SignaturePackingError that denotes the first element that cannot
-/// be placed, or success if all eligible elements were placed.
+/// See llvm/docs/DirectX/SemanticSignatures.md#stacked-packing for details.
LLVM_ABI Error
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
>From f037a272e6fc41fb66b06be210246194cb75e785 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 18:48:42 +0000
Subject: [PATCH 06/17] review: rename expected to verify
---
.../HLSLSemanticSignaturePackingTest.cpp | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index a1fd495638613..57908a3a8dcc0 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -72,7 +72,7 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
}
- void expectPacking(const TestConfig &Config, unsigned ExpectedRows,
+ void verifyPacking(const TestConfig &Config, unsigned ExpectedRows,
std::initializer_list<ExpectedLocation> Locations) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
ASSERT_EQ(Elements.size(), Locations.size());
@@ -93,7 +93,7 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
}
}
- void expectPackingError(const TestConfig &Config,
+ void verifyPackingError(const TestConfig &Config,
SignaturePackingError::ErrorKind ExpectedKind,
unsigned ExpectedElementIndex) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
@@ -170,7 +170,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, SkipsNotAllocatedElements) {
dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined}});
// Expected layout: no registers are used.
- expectPacking(Config, /*ExpectedRows=*/0,
+ verifyPacking(Config, /*ExpectedRows=*/0,
{Unallocated, Unallocated, Unallocated});
}
@@ -196,7 +196,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, StacksInDeclarationOrder) {
// reg0: VertexID.x | unused.yzw
// reg1: Data.xy | unused.zw
// reg2: ClipDistance.xyz | unused.w
- expectPacking(
+ verifyPacking(
Config, /*ExpectedRows=*/3,
{{/*Row=*/0, /*Col=*/0}, {/*Row=*/1, /*Col=*/0}, {/*Row=*/2, /*Col=*/0}});
}
@@ -226,7 +226,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, DoesNotCoPackElements) {
// reg1: B.x | unused.yzw
// reg2: C.x | unused.yzw
// reg3: D.x | unused.yzw
- expectPacking(Config, /*ExpectedRows=*/4,
+ verifyPacking(Config, /*ExpectedRows=*/4,
{{/*Row=*/0, /*Col=*/0},
{/*Row=*/1, /*Col=*/0},
{/*Row=*/2, /*Col=*/0},
@@ -257,7 +257,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, StacksMultiRowElements) {
// reg3: B[0].xyz | unused.w
// reg4: B[1].xyz | unused.w
// reg5: C.xyzw
- expectPacking(
+ verifyPacking(
Config, /*ExpectedRows=*/6,
{{/*Row=*/0, /*Col=*/0}, {/*Row=*/3, /*Col=*/0}, {/*Row=*/5, /*Col=*/0}});
}
@@ -276,7 +276,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, ExactlyFillsSignature) {
// Expected layout:
// reg0-31: A[0-31].xyzw
- expectPacking(Config, /*ExpectedRows=*/MaxSignatureRows,
+ verifyPacking(Config, /*ExpectedRows=*/MaxSignatureRows,
{{/*Row=*/0, /*Col=*/0}});
}
@@ -300,7 +300,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsSignatureOverflow) {
dxbc::PSV::InterpolationMode::Linear});
// The last element is the one that no longer fits.
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ verifyPackingError(Config, SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/MaxSignatureRows);
}
@@ -316,7 +316,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsSingleElementOverflow) {
/*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
dxbc::PSV::InterpolationMode::Linear}});
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ verifyPackingError(Config, SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/0);
}
@@ -335,7 +335,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsMultiRowSignatureOverflow) {
/*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
dxbc::PSV::InterpolationMode::Linear}});
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ verifyPackingError(Config, SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/1);
}
>From f9195f475e3372b53aef32617ba74095d8a29c23 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 18:50:28 +0000
Subject: [PATCH 07/17] review: update doc reference
---
llvm/docs/DirectX/SemanticSignatures.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/DirectX/SemanticSignatures.md b/llvm/docs/DirectX/SemanticSignatures.md
index 985a6d596a9e5..a30199f8ccf45 100644
--- a/llvm/docs/DirectX/SemanticSignatures.md
+++ b/llvm/docs/DirectX/SemanticSignatures.md
@@ -141,8 +141,9 @@ interpretations accepted by a packing algorithm are assigned locations
according to that algorithm's rules. If an eligible element cannot be placed,
packing returns a `SignaturePackingError` identifying the element that failed.
-The packing APIs and their in-memory element representation are declared in
-[SemanticSignaturePacking.h].
+The packing APIs are declared in [SemanticSignaturePacking.h], and the
+in-memory element representation they operate on is declared in
+[SemanticSignatures.h].
[SemanticSignaturePacking.h]: https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
>From 1333db46be701be2bb24ecd2be1af5a45d11bc4b Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 18:56:12 +0000
Subject: [PATCH 08/17] review: add assert of non-vertex shader input
invocation
---
llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 59289497dce2b..d4ddaf19fbdfb 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -31,6 +31,9 @@ void SignaturePackingError::log(raw_ostream &OS) const {
Error llvm::hlsl::packSignatureStacked(
MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy) {
+ assert(ShaderStage == Triple::Vertex && IOTy == IOType::In &&
+ "stacked packing is only valid for a vertex shader input signature");
+
unsigned NextRow = 0;
for (const auto &[Index, Element] : enumerate(Elements)) {
assert(Element.StartRow == UnallocatedRow &&
>From 89c618d4c046ea15ba83a151cefe3eea3cd7da1e Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 19:03:08 +0000
Subject: [PATCH 09/17] review: update not allocated test to show the row is
not used
adds the ViewID semantic table for the purpose of testing it validly
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 16 ++++++++++
.../HLSLSemanticSignaturePackingTest.cpp | 31 ++++++++++---------
2 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 7c4480075d6af..fc13cb76f821b 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -85,6 +85,22 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
};
return Stages;
}
+ case dxbc::PSV::SemanticKind::ViewID: {
+ static constexpr IOType InOrPatchConstant =
+ static_cast<IOType>(IOType::In | IOType::PatchConstantOrPrimitive);
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Vertex, IOType::In, SemanticInterpretation::NotAllocated},
+ {Triple::Hull, InOrPatchConstant, SemanticInterpretation::NotAllocated},
+ {Triple::Domain, InOrPatchConstant,
+ SemanticInterpretation::NotAllocated},
+ {Triple::Geometry, IOType::In, SemanticInterpretation::NotAllocated},
+ {Triple::Pixel, IOType::In, SemanticInterpretation::NotAllocated},
+ {Triple::Mesh, InOrPatchConstant, SemanticInterpretation::NotAllocated},
+ {Triple::Amplification, IOType::In,
+ SemanticInterpretation::NotAllocated},
+ };
+ return Stages;
+ }
case dxbc::PSV::SemanticKind::Target: {
static constexpr SemanticStageInfo Stages[] = {
{Triple::Pixel, IOType::Out, SemanticInterpretation::Target}};
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index 57908a3a8dcc0..eab2f04ff4c98 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -153,25 +153,28 @@ TEST_F(HLSLSemanticSignaturePackingTest, CreatesSignatureFromConfig) {
TEST_F(HLSLSemanticSignaturePackingTest, SkipsNotAllocatedElements) {
// Semantics accessed through dedicated intrinsics do not consume signature
- // rows and remain unallocated.
+ // rows and remain unallocated. The elements around them are packed as if the
+ // unallocated element was not declared at all.
- // struct CSIn {
- // uint3 DispatchThreadID : SV_DispatchThreadID;
- // uint3 GroupID : SV_GroupID;
- // uint GroupIndex : SV_GroupIndex;
+ // struct VSIn {
+ // float2 A : A;
+ // uint ViewID : SV_ViewID;
+ // float3 B : B;
// };
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,
+ Triple::EnvironmentType::Vertex, IOType::In,
+ {{dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/2,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+ {dxbc::PSV::SemanticKind::ViewID, /*Rows=*/1, /*Cols=*/1,
dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined},
- {dxbc::PSV::SemanticKind::GroupIndex, /*Rows=*/1, /*Cols=*/1,
- dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined}});
+ {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/1, /*Cols=*/3,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
- // Expected layout: no registers are used.
- verifyPacking(Config, /*ExpectedRows=*/0,
- {Unallocated, Unallocated, Unallocated});
+ // Expected layout:
+ // reg0: A.xy | unused.zw
+ // reg1: B.xyz | unused.w
+ verifyPacking(Config, /*ExpectedRows=*/2,
+ {{/*Row=*/0, /*Col=*/0}, Unallocated, {/*Row=*/1, /*Col=*/0}});
}
TEST_F(HLSLSemanticSignaturePackingTest, StacksInDeclarationOrder) {
>From 19ae1085f721804db6fefa91c0659d486cd9fcef Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 19:55:53 +0000
Subject: [PATCH 10/17] review: remove assert of error type
---
.../Frontend/HLSLSemanticSignaturePackingTest.cpp | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index eab2f04ff4c98..a72f2f6e5826b 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -102,11 +102,16 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
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);
- });
+ handleAllErrors(
+ std::move(E),
+ [&](const SignaturePackingError &PackingErr) {
+ EXPECT_EQ(PackingErr.getErrorKind(), ExpectedKind);
+ EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
+ },
+ [](const ErrorInfoBase &Other) {
+ ADD_FAILURE() << "expected a SignaturePackingError, got: "
+ << Other.message();
+ });
}
};
>From 0731dd3aa0e27a700675746982daba0e108c54b1 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 19:57:24 +0000
Subject: [PATCH 11/17] review: mark class with llvm_abi
---
llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 14cd477994d68..828195031ce6f 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -25,13 +25,14 @@ static constexpr unsigned MaxSignatureRows = 32;
static constexpr unsigned MaxSignatureCols = 4;
/// Denotes the element that could not be packed and why.
-class SignaturePackingError : public ErrorInfo<SignaturePackingError> {
+class LLVM_ABI SignaturePackingError
+ : public ErrorInfo<SignaturePackingError> {
public:
enum ErrorKind {
SignatureOverflow,
};
- LLVM_ABI static char ID;
+ static char ID;
SignaturePackingError(ErrorKind Kind, unsigned ElementIndex)
: Kind(Kind), ElementIndex(ElementIndex) {}
@@ -39,7 +40,7 @@ class SignaturePackingError : public ErrorInfo<SignaturePackingError> {
ErrorKind getErrorKind() const { return Kind; }
unsigned getElementIndex() const { return ElementIndex; }
- LLVM_ABI void log(raw_ostream &OS) const override;
+ void log(raw_ostream &OS) const override;
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
>From f301fb9a11837252870ee34246e2aa0b30c0761e Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 19:58:16 +0000
Subject: [PATCH 12/17] review: remove magic number
---
llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index d4ddaf19fbdfb..beb05f2c436c3 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -22,7 +22,7 @@ char SignaturePackingError::ID;
void SignaturePackingError::log(raw_ostream &OS) const {
switch (Kind) {
case SignatureOverflow:
- OS << "signature elements do not fit in 32 rows";
+ OS << "signature elements do not fit in " << MaxSignatureRows << " rows";
break;
}
OS << " (element " << ElementIndex << ")";
>From 2f98b3ef30d33882316d1c2685f00adc8dc52184 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 19:58:40 +0000
Subject: [PATCH 13/17] review: remove misleading const
---
llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index beb05f2c436c3..739d4b46e6988 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -35,7 +35,7 @@ Error llvm::hlsl::packSignatureStacked(
"stacked packing is only valid for a vertex shader input signature");
unsigned NextRow = 0;
- for (const auto &[Index, Element] : enumerate(Elements)) {
+ for (auto &&[Index, Element] : enumerate(Elements)) {
assert(Element.StartRow == UnallocatedRow &&
Element.StartCol == UnallocatedCol && "already allocated?");
assert(Element.Rows > 0 && "signature element must have at least one row");
>From 538ce4ada60086f347d1d5f098969f03603830b7 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 20:03:48 +0000
Subject: [PATCH 14/17] review: add failure comment
partially allocated is good so that diagnostics can provide richer info
when wanted
---
llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 828195031ce6f..9143d01d31094 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -54,6 +54,11 @@ class LLVM_ABI SignaturePackingError
/// Packs eligible signature elements into consecutive rows.
///
/// See llvm/docs/DirectX/SemanticSignatures.md#stacked-packing for details.
+///
+/// On failure, Elements is left partially packed: the elements preceding the
+/// one reported by the returned SignaturePackingError keep the locations
+/// they were assigned, while that element and the ones following it retain the
+/// unallocated row and column sentinels.
LLVM_ABI Error
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
>From 2c35388619dbde079a3bfda52493cb12974a9bfc Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 20:06:42 +0000
Subject: [PATCH 15/17] review: return number of rows packed into
---
.../Frontend/HLSL/SemanticSignaturePacking.h | 2 +-
.../HLSL/SemanticSignaturePacking.cpp | 4 ++--
.../HLSLSemanticSignaturePackingTest.cpp | 21 ++++++++-----------
3 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 9143d01d31094..522b8b76a5592 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -59,7 +59,7 @@ class LLVM_ABI SignaturePackingError
/// one reported by the returned SignaturePackingError keep the locations
/// they were assigned, while that element and the ones following it retain the
/// unallocated row and column sentinels.
-LLVM_ABI Error
+LLVM_ABI Expected<unsigned>
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 739d4b46e6988..786170fc71f66 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -28,7 +28,7 @@ void SignaturePackingError::log(raw_ostream &OS) const {
OS << " (element " << ElementIndex << ")";
}
-Error llvm::hlsl::packSignatureStacked(
+Expected<unsigned> llvm::hlsl::packSignatureStacked(
MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy) {
assert(ShaderStage == Triple::Vertex && IOTy == IOType::In &&
@@ -62,5 +62,5 @@ Error llvm::hlsl::packSignatureStacked(
NextRow += Element.Rows;
}
- return Error::success();
+ return NextRow;
}
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index a72f2f6e5826b..5ee3223313b50 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -67,8 +67,9 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
return Elements;
}
- Error packStacked(SmallVectorImpl<SemanticSignatureElement> &Elements,
- const TestConfig &Config) {
+ Expected<unsigned>
+ packStacked(SmallVectorImpl<SemanticSignatureElement> &Elements,
+ const TestConfig &Config) {
return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
}
@@ -77,13 +78,9 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
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);
+ Expected<unsigned> Rows = packStacked(Elements, Config);
+ ASSERT_THAT_EXPECTED(Rows, Succeeded());
+ EXPECT_EQ(*Rows, ExpectedRows);
unsigned Index = 0;
for (ExpectedLocation Location : Locations) {
@@ -97,13 +94,13 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
SignaturePackingError::ErrorKind ExpectedKind,
unsigned ExpectedElementIndex) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
- Error E = packStacked(Elements, Config);
- if (!E) {
+ Expected<unsigned> Rows = packStacked(Elements, Config);
+ if (Rows) {
ADD_FAILURE() << "expected a SignaturePackingError";
return;
}
handleAllErrors(
- std::move(E),
+ Rows.takeError(),
[&](const SignaturePackingError &PackingErr) {
EXPECT_EQ(PackingErr.getErrorKind(), ExpectedKind);
EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
>From 5c084c38c5079df28eb6bf4040926bbd6401cdd1 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 20:09:33 +0000
Subject: [PATCH 16/17] review: clarify that the assert is sufficient handling
---
llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 786170fc71f66..278c6a4f06d7d 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -50,7 +50,8 @@ Expected<unsigned> llvm::hlsl::packSignatureStacked(
assert((Interpretation == SemanticInterpretation::Arbitrary ||
Interpretation == SemanticInterpretation::SV ||
Interpretation == SemanticInterpretation::SGV) &&
- "unexpected semantic interpretation for stacked packing");
+ "unexpected semantic interpretation for stacked packing, should "
+ "have been diagnosed by Sema");
if (Element.Rows > MaxSignatureRows - NextRow)
return make_error<SignaturePackingError>(
>From cd7e3beaea8c69fb536a34b903e3980ed4b8b084 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Thu, 10 Sep 2026 18:50:31 +0000
Subject: [PATCH 17/17] self-review: remove static_cast
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index fc13cb76f821b..0d17004b6a8c0 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -87,7 +87,7 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
}
case dxbc::PSV::SemanticKind::ViewID: {
static constexpr IOType InOrPatchConstant =
- static_cast<IOType>(IOType::In | IOType::PatchConstantOrPrimitive);
+ IOType::In | IOType::PatchConstantOrPrimitive;
static constexpr SemanticStageInfo Stages[] = {
{Triple::Vertex, IOType::In, SemanticInterpretation::NotAllocated},
{Triple::Hull, InOrPatchConstant, SemanticInterpretation::NotAllocated},
More information about the llvm-commits
mailing list