https://github.com/inbelic created https://github.com/llvm/llvm-project/pull/218060
This pr defines a testing harness for the packing algorithms of semantic signatures and then implements the stacked packing algorithm. Resolves: https://github.com/llvm/llvm-project/issues/205875 >From 01bba9a0ea1389d68409ec914ef79d2cc898567a Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 19:00:44 +0000 Subject: [PATCH 1/6] review: small typo --- llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h index 8e355a05cb86c..3f2d2946d44eb 100644 --- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h +++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h @@ -41,6 +41,8 @@ enum IOType { InOut = 0b011, PatchConstantOrPrimitive = 0b100, All = 0b111, + + LLVM_MARK_AS_BITMASK_ENUM(PatchConstantOrPrimitive), }; enum class SemanticInterpretation { >From 37c341bf967db45e877006c36ccb051ea6d7aa20 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 19:22:52 +0000 Subject: [PATCH 2/6] 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 8976dd1b2f737..69478ac630fd8 100644 --- a/llvm/unittests/Frontend/CMakeLists.txt +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_unittest(LLVMFrontendTests HLSLBindingTest.cpp HLSLRootSignatureDumpTest.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 c28549f970d15443aaf7943b6f0c92d4f6ef0cbf Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 19:26:01 +0000 Subject: [PATCH 3/6] 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 26f935fad6be1d10bcc8c9b0dd1f4a1c0e9195d6 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 19:57:20 +0000 Subject: [PATCH 4/6] 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 03b65b6c2aed57577f7d322c4c0dcbbb6587758b Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 20:09:34 +0000 Subject: [PATCH 5/6] 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 baa1ccd76efa972019231b2b8955a448e52987c4 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Fri, 21 Aug 2026 20:20:15 +0000 Subject: [PATCH 6/6] 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); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
