[clang] [llvm] [HLSLSemanticSignatures] Implement the indexed packing of elements (PR #218061)
Finn Plummer via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 21 16:02:54 PDT 2026
https://github.com/inbelic created https://github.com/llvm/llvm-project/pull/218061
Resolves: #205877
Assisted by: Claude Opus 5 and GPT-5.6 Sol
>From e52b84aa7ee4c878202e01826bfaa40de0128bf4 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:40:58 +0000
Subject: [PATCH 01/28] nfc: move def to semasig
---
clang/include/clang/Sema/SemaHLSL.h | 21 +++++--------------
clang/lib/Sema/SemaHLSL.cpp | 2 ++
.../llvm/Frontend/HLSL/SemanticSignatures.h | 15 +++++++++++++
3 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 8928524e49783..0f834df21c40d 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -23,6 +23,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Frontend/HLSL/SemanticSignatures.h"
#include "llvm/TargetParser/Triple.h"
#include <initializer_list>
@@ -289,14 +290,6 @@ class SemaHLSL : public SemaBase {
std::optional<uint32_t> Index = std::nullopt;
};
- // Bitmask used to recall if the current semantic subtree is
- // input, output or inout.
- enum IOType {
- In = 0b01,
- Out = 0b10,
- InOut = 0b11,
- };
-
// The context shared by all semantics with the same IOType during
// flattening.
struct SemanticContext {
@@ -307,12 +300,7 @@ class SemaHLSL : public SemaBase {
// index collisions.
llvm::StringSet<> ActiveSemantics = {};
// The IOType of this semantic set.
- IOType CurrentIOType;
- };
-
- struct SemanticStageInfo {
- llvm::Triple::EnvironmentType Stage;
- IOType AllowedIOTypesMask;
+ llvm::hlsl::IOType CurrentIOType;
};
private:
@@ -343,8 +331,9 @@ class SemaHLSL : public SemaBase {
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
void diagnoseSemanticStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
- std::initializer_list<SemanticStageInfo> AllowedStages);
+ const Attr *A, llvm::Triple::EnvironmentType Stage,
+ llvm::hlsl::IOType CurrentIOType,
+ std::initializer_list<llvm::hlsl::SemanticStageInfo> AllowedStages);
void handleGlobalStructOrArrayOfWithResources(VarDecl *VD);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 184339044e5bf..6bcd6d9182e44 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -54,6 +54,8 @@
using namespace clang;
using namespace clang::hlsl;
+using llvm::hlsl::IOType;
+using llvm::hlsl::SemanticStageInfo;
using RegisterType = HLSLResourceBindingAttr::RegisterType;
static CXXRecordDecl *createHostLayoutStruct(Sema &S,
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 0d0da50189e53..042b4afdf6faf 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -21,6 +21,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/Triple.h"
#include <cstdint>
namespace llvm {
@@ -32,6 +33,20 @@ namespace hlsl {
// Definitions of the in-memory data layout structures
+// Bitmask denoting whether a semantic is an input, output, or a value that is
+// constant across a patch (hull/domain shaders) or primitive (mesh shaders).
+enum IOType {
+ In = 0b001,
+ Out = 0b010,
+ InOut = 0b011,
+ PatchConstantOrPrimitive = 0b100,
+};
+
+struct SemanticStageInfo {
+ Triple::EnvironmentType Stage;
+ IOType AllowedIOTypesMask;
+};
+
// Sentinel values denoting that an element is unallocated
static constexpr uint32_t UnallocatedRow = ~0U;
static constexpr uint8_t UnallocatedCol = 0xFF;
>From 0a79bad0fc01ced54e83f66e55fa3d289ae02229 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:42:51 +0000
Subject: [PATCH 02/28] extend to for patch constants
---
clang/lib/Sema/SemaHLSL.cpp | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6bcd6d9182e44..5ed6d4632d8ee 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1158,12 +1158,19 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
ValidType.push_back("input");
if (Case.AllowedIOTypesMask & IOType::Out)
ValidType.push_back("output");
+ if (Case.AllowedIOTypesMask & IOType::PatchConstantOrPrimitive)
+ ValidType.push_back("patch constant or primitive");
return std::string(
HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) +
" " + join(ValidType, "/");
});
+ StringRef CurrentIOTypeName = "patch constant or primitive";
+ if (CurrentIOType & IOType::In)
+ CurrentIOTypeName = "input";
+ else if (CurrentIOType & IOType::Out)
+ CurrentIOTypeName = "output";
Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
- << A->getAttrName() << (CurrentIOType & IOType::In ? "input" : "output")
+ << A->getAttrName() << CurrentIOTypeName
<< llvm::Triple::getEnvironmentTypeName(Case.Stage)
<< join(ValidCases, ", ");
return;
>From a65ec0ce3b98b7330cc61d0c7b47f5f51a0cc497 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:54:45 +0000
Subject: [PATCH 03/28] define common info getter
---
llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 3 +++
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 042b4afdf6faf..5749f0b0538a9 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -119,6 +119,9 @@ struct SemanticSignatureElement {
LLVM_ABI dxbc::PSV::SemanticKind getSemanticKind(StringRef SemanticName);
+LLVM_ABI ArrayRef<SemanticStageInfo>
+getAvailableStages(dxbc::PSV::SemanticKind SemanticKind);
+
} // namespace hlsl
} // namespace llvm
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 48d68c68946d7..72a5366bafb70 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -17,6 +17,7 @@
#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;
@@ -54,6 +55,11 @@ dxbc::PSV::SemanticKind hlsl::getSemanticKind(StringRef SemanticName) {
return dxbc::PSV::SemanticKind::Invalid;
}
+ArrayRef<SemanticStageInfo>
+hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
+ llvm_unreachable("available stages for given semantic kind are not handled");
+}
+
Expected<SemanticSignatureElement>
SemanticSignatureElement::fromMetadata(const MDNode *Node) {
// Operand positions within a signature element metadata node.
>From cf60ce9fdef9340dc1860e6bce45d521bc4d8d7b Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:56:51 +0000
Subject: [PATCH 04/28] categorize compute semantics
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 72a5366bafb70..68f8e20b4ff8b 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -57,7 +57,19 @@ dxbc::PSV::SemanticKind hlsl::getSemanticKind(StringRef SemanticName) {
ArrayRef<SemanticStageInfo>
hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
- llvm_unreachable("available stages for given semantic kind are not handled");
+ switch (SemanticKind) {
+ case dxbc::PSV::SemanticKind::DispatchThreadID:
+ case dxbc::PSV::SemanticKind::GroupID:
+ case dxbc::PSV::SemanticKind::GroupIndex:
+ case dxbc::PSV::SemanticKind::GroupThreadID: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Compute, IOType::In}};
+ return Stages;
+ }
+ default:
+ llvm_unreachable(
+ "available stages for given semantic kind are not handled");
+ }
}
Expected<SemanticSignatureElement>
>From 66534f60a860bc77aafbe42cb93d3fda558eac69 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:56:57 +0000
Subject: [PATCH 05/28] categorize target
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 68f8e20b4ff8b..7b631fa22d545 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -66,6 +66,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
{Triple::Compute, IOType::In}};
return Stages;
}
+ case dxbc::PSV::SemanticKind::Target: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Pixel, IOType::Out}};
+ return Stages;
+ }
default:
llvm_unreachable(
"available stages for given semantic kind are not handled");
>From ab60f591d0e0b153a3c4b73e7b99f4a8c5eb980e Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:03 +0000
Subject: [PATCH 06/28] categorize vertexid
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 7b631fa22d545..4704f2859be82 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -71,6 +71,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
{Triple::Pixel, IOType::Out}};
return Stages;
}
+ case dxbc::PSV::SemanticKind::VertexID: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Vertex, IOType::In}};
+ return Stages;
+ }
default:
llvm_unreachable(
"available stages for given semantic kind are not handled");
>From 910bf701476377dab90ea5ee78a70b3781862a1b Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:12 +0000
Subject: [PATCH 07/28] categorize position
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 4704f2859be82..1ac635e50d635 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -76,6 +76,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
{Triple::Vertex, IOType::In}};
return Stages;
}
+ case dxbc::PSV::SemanticKind::Position: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Vertex, IOType::InOut}, {Triple::Pixel, IOType::In}};
+ return Stages;
+ }
default:
llvm_unreachable(
"available stages for given semantic kind are not handled");
>From f7df75f0ba00cf358cb795755f2413b730b81c1c Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:26 +0000
Subject: [PATCH 08/28] categorize arbitrary
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 1ac635e50d635..480e6c0f122d4 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -58,6 +58,18 @@ dxbc::PSV::SemanticKind hlsl::getSemanticKind(StringRef SemanticName) {
ArrayRef<SemanticStageInfo>
hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
switch (SemanticKind) {
+ case dxbc::PSV::SemanticKind::Arbitrary: {
+ static constexpr IOType AllIOTypes =
+ static_cast<IOType>(IOType::InOut | IOType::PatchConstantOrPrimitive);
+ static constexpr IOType OutOrPatchConstant =
+ static_cast<IOType>(IOType::Out | IOType::PatchConstantOrPrimitive);
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Vertex, IOType::InOut}, {Triple::Geometry, IOType::InOut},
+ {Triple::Hull, AllIOTypes}, {Triple::Domain, AllIOTypes},
+ {Triple::Pixel, IOType::In}, {Triple::Mesh, OutOrPatchConstant},
+ };
+ return Stages;
+ }
case dxbc::PSV::SemanticKind::DispatchThreadID:
case dxbc::PSV::SemanticKind::GroupID:
case dxbc::PSV::SemanticKind::GroupIndex:
>From 6c2656c0136147e770aa72ed54ab99a4c8b08a95 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:38 +0000
Subject: [PATCH 09/28] categorize clip/cull
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 480e6c0f122d4..58e44fe756a75 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -93,6 +93,17 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
{Triple::Vertex, IOType::InOut}, {Triple::Pixel, IOType::In}};
return Stages;
}
+ case dxbc::PSV::SemanticKind::ClipDistance:
+ case dxbc::PSV::SemanticKind::CullDistance: {
+ static constexpr IOType AllIOTypes =
+ static_cast<IOType>(IOType::InOut | IOType::PatchConstantOrPrimitive);
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Vertex, IOType::InOut}, {Triple::Hull, AllIOTypes},
+ {Triple::Domain, AllIOTypes}, {Triple::Geometry, IOType::InOut},
+ {Triple::Pixel, IOType::In}, {Triple::Mesh, IOType::Out},
+ };
+ return Stages;
+ }
default:
llvm_unreachable(
"available stages for given semantic kind are not handled");
>From 5e5f6474d507f3110de7b9ef2e1015111b113d3f Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:46 +0000
Subject: [PATCH 10/28] categorize tess factors
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 58e44fe756a75..f28979bf702f6 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -104,6 +104,14 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
};
return Stages;
}
+ case dxbc::PSV::SemanticKind::TessFactor:
+ case dxbc::PSV::SemanticKind::InsideTessFactor: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Hull, IOType::PatchConstantOrPrimitive},
+ {Triple::Domain, IOType::PatchConstantOrPrimitive},
+ };
+ return Stages;
+ }
default:
llvm_unreachable(
"available stages for given semantic kind are not handled");
>From 0be9a96259a637132ee32e7b388a228590c969f9 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:57:53 +0000
Subject: [PATCH 11/28] categorize front-face semantics
---
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index f28979bf702f6..5971275fd0dde 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -88,6 +88,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
{Triple::Vertex, IOType::In}};
return Stages;
}
+ case dxbc::PSV::SemanticKind::IsFrontFace: {
+ static constexpr SemanticStageInfo Stages[] = {
+ {Triple::Geometry, IOType::Out}, {Triple::Pixel, IOType::In}};
+ return Stages;
+ }
case dxbc::PSV::SemanticKind::Position: {
static constexpr SemanticStageInfo Stages[] = {
{Triple::Vertex, IOType::InOut}, {Triple::Pixel, IOType::In}};
>From 03e6d10cf6c65923c5b27c556db3b931d7ada19b Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:59:05 +0000
Subject: [PATCH 12/28] nfc: define all in enum
---
llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 1 +
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 10 +++-------
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 5749f0b0538a9..0af56b46d60b1 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -40,6 +40,7 @@ enum IOType {
Out = 0b010,
InOut = 0b011,
PatchConstantOrPrimitive = 0b100,
+ All = 0b111,
};
struct SemanticStageInfo {
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 5971275fd0dde..09fd5c4ee6744 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -59,13 +59,11 @@ ArrayRef<SemanticStageInfo>
hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
switch (SemanticKind) {
case dxbc::PSV::SemanticKind::Arbitrary: {
- static constexpr IOType AllIOTypes =
- static_cast<IOType>(IOType::InOut | IOType::PatchConstantOrPrimitive);
static constexpr IOType OutOrPatchConstant =
static_cast<IOType>(IOType::Out | IOType::PatchConstantOrPrimitive);
static constexpr SemanticStageInfo Stages[] = {
{Triple::Vertex, IOType::InOut}, {Triple::Geometry, IOType::InOut},
- {Triple::Hull, AllIOTypes}, {Triple::Domain, AllIOTypes},
+ {Triple::Hull, IOType::All}, {Triple::Domain, IOType::All},
{Triple::Pixel, IOType::In}, {Triple::Mesh, OutOrPatchConstant},
};
return Stages;
@@ -100,11 +98,9 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
}
case dxbc::PSV::SemanticKind::ClipDistance:
case dxbc::PSV::SemanticKind::CullDistance: {
- static constexpr IOType AllIOTypes =
- static_cast<IOType>(IOType::InOut | IOType::PatchConstantOrPrimitive);
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Vertex, IOType::InOut}, {Triple::Hull, AllIOTypes},
- {Triple::Domain, AllIOTypes}, {Triple::Geometry, IOType::InOut},
+ {Triple::Vertex, IOType::InOut}, {Triple::Hull, IOType::All},
+ {Triple::Domain, IOType::All}, {Triple::Geometry, IOType::InOut},
{Triple::Pixel, IOType::In}, {Triple::Mesh, IOType::Out},
};
return Stages;
>From a127af01ebc58492dbd2a9af1b7233b295e874e6 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 17:59:46 +0000
Subject: [PATCH 13/28] define interpretation kinds enum
---
llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 0af56b46d60b1..85ddabe266c1e 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -43,6 +43,17 @@ enum IOType {
All = 0b111,
};
+enum class SemanticInterpretation {
+ Invalid,
+ NotAllocated,
+ Arbitrary,
+ SV,
+ SGV,
+ ClipCull,
+ TessFactor,
+ Target,
+};
+
struct SemanticStageInfo {
Triple::EnvironmentType Stage;
IOType AllowedIOTypesMask;
>From afd27adc4a2c4d149948c3869338ef17cc2f7f4e Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 18:02:29 +0000
Subject: [PATCH 14/28] include interpretation in semantic stage info
---
.../llvm/Frontend/HLSL/SemanticSignatures.h | 1 +
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 42 +++++++++++++------
2 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 85ddabe266c1e..30d04f0da098e 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -57,6 +57,7 @@ enum class SemanticInterpretation {
struct SemanticStageInfo {
Triple::EnvironmentType Stage;
IOType AllowedIOTypesMask;
+ SemanticInterpretation Interpretation;
};
// Sentinel values denoting that an element is unallocated
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 09fd5c4ee6744..9361f57115f5b 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -62,9 +62,12 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
static constexpr IOType OutOrPatchConstant =
static_cast<IOType>(IOType::Out | IOType::PatchConstantOrPrimitive);
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Vertex, IOType::InOut}, {Triple::Geometry, IOType::InOut},
- {Triple::Hull, IOType::All}, {Triple::Domain, IOType::All},
- {Triple::Pixel, IOType::In}, {Triple::Mesh, OutOrPatchConstant},
+ {Triple::Vertex, IOType::InOut, SemanticInterpretation::Arbitrary},
+ {Triple::Geometry, IOType::InOut, SemanticInterpretation::Arbitrary},
+ {Triple::Hull, IOType::All, SemanticInterpretation::Arbitrary},
+ {Triple::Domain, IOType::All, SemanticInterpretation::Arbitrary},
+ {Triple::Pixel, IOType::In, SemanticInterpretation::Arbitrary},
+ {Triple::Mesh, OutOrPatchConstant, SemanticInterpretation::Arbitrary},
};
return Stages;
}
@@ -73,43 +76,56 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
case dxbc::PSV::SemanticKind::GroupIndex:
case dxbc::PSV::SemanticKind::GroupThreadID: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Compute, IOType::In}};
+ {Triple::Compute, IOType::In, SemanticInterpretation::NotAllocated}};
return Stages;
}
case dxbc::PSV::SemanticKind::Target: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Pixel, IOType::Out}};
+ {Triple::Pixel, IOType::Out, SemanticInterpretation::Target}};
return Stages;
}
case dxbc::PSV::SemanticKind::VertexID: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Vertex, IOType::In}};
+ {Triple::Vertex, IOType::In, SemanticInterpretation::SV}};
return Stages;
}
case dxbc::PSV::SemanticKind::IsFrontFace: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Geometry, IOType::Out}, {Triple::Pixel, IOType::In}};
+ {Triple::Geometry, IOType::Out, SemanticInterpretation::SGV},
+ {Triple::Pixel, IOType::In, SemanticInterpretation::SGV}};
return Stages;
}
case dxbc::PSV::SemanticKind::Position: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Vertex, IOType::InOut}, {Triple::Pixel, IOType::In}};
+ {Triple::Vertex, IOType::In, SemanticInterpretation::Arbitrary},
+ {Triple::Vertex, IOType::Out, SemanticInterpretation::SV},
+ {Triple::Pixel, IOType::In, SemanticInterpretation::SV}};
return Stages;
}
case dxbc::PSV::SemanticKind::ClipDistance:
case dxbc::PSV::SemanticKind::CullDistance: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Vertex, IOType::InOut}, {Triple::Hull, IOType::All},
- {Triple::Domain, IOType::All}, {Triple::Geometry, IOType::InOut},
- {Triple::Pixel, IOType::In}, {Triple::Mesh, IOType::Out},
+ {Triple::Vertex, IOType::In, SemanticInterpretation::Arbitrary},
+ {Triple::Vertex, IOType::Out, SemanticInterpretation::ClipCull},
+ {Triple::Hull, IOType::InOut, SemanticInterpretation::ClipCull},
+ {Triple::Hull, IOType::PatchConstantOrPrimitive,
+ SemanticInterpretation::Arbitrary},
+ {Triple::Domain, IOType::InOut, SemanticInterpretation::ClipCull},
+ {Triple::Domain, IOType::PatchConstantOrPrimitive,
+ SemanticInterpretation::Arbitrary},
+ {Triple::Geometry, IOType::InOut, SemanticInterpretation::ClipCull},
+ {Triple::Pixel, IOType::In, SemanticInterpretation::ClipCull},
+ {Triple::Mesh, IOType::Out, SemanticInterpretation::ClipCull},
};
return Stages;
}
case dxbc::PSV::SemanticKind::TessFactor:
case dxbc::PSV::SemanticKind::InsideTessFactor: {
static constexpr SemanticStageInfo Stages[] = {
- {Triple::Hull, IOType::PatchConstantOrPrimitive},
- {Triple::Domain, IOType::PatchConstantOrPrimitive},
+ {Triple::Hull, IOType::PatchConstantOrPrimitive,
+ SemanticInterpretation::TessFactor},
+ {Triple::Domain, IOType::PatchConstantOrPrimitive,
+ SemanticInterpretation::TessFactor},
};
return Stages;
}
>From e22de0a6a70d27808e867748a216a9b08381935f Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 18:10:44 +0000
Subject: [PATCH 15/28] add kind helper
---
llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 4 ++++
llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 9 +++++++++
2 files changed, 13 insertions(+)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 30d04f0da098e..8e355a05cb86c 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -135,6 +135,10 @@ LLVM_ABI dxbc::PSV::SemanticKind getSemanticKind(StringRef SemanticName);
LLVM_ABI ArrayRef<SemanticStageInfo>
getAvailableStages(dxbc::PSV::SemanticKind SemanticKind);
+LLVM_ABI SemanticInterpretation
+getInterpretationKind(dxbc::PSV::SemanticKind SemanticKind,
+ Triple::EnvironmentType ShaderStage, IOType IOTy);
+
} // namespace hlsl
} // namespace llvm
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 9361f57115f5b..7a0e3a21564d4 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -135,6 +135,15 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
}
}
+SemanticInterpretation
+hlsl::getInterpretationKind(dxbc::PSV::SemanticKind SemanticKind,
+ Triple::EnvironmentType ShaderStage, IOType IOTy) {
+ for (const SemanticStageInfo &Info : getAvailableStages(SemanticKind))
+ if (Info.Stage == ShaderStage && (Info.AllowedIOTypesMask & IOTy))
+ return Info.Interpretation;
+ return SemanticInterpretation::Invalid;
+}
+
Expected<SemanticSignatureElement>
SemanticSignatureElement::fromMetadata(const MDNode *Node) {
// Operand positions within a signature element metadata node.
>From 11ef54bdc4fdbb1084f4c6b043a54beab6e52122 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 18:17:04 +0000
Subject: [PATCH 16/28] nfc: refactor semahlsl to use look-up table
---
clang/include/clang/Sema/SemaHLSL.h | 3 +-
clang/lib/Sema/SemaHLSL.cpp | 73 +++++++++----------
.../CodeGenHLSL/semantics/SV_Position.ps.hlsl | 4 +-
.../semantics/semantic.array.output.hlsl | 2 +-
.../Availability/attr-availability-pixel.hlsl | 2 +-
.../Semantics/arbitrary.ps.output.hlsl | 7 ++
.../Semantics/position.ps.struct.hlsl | 5 +-
.../Semantics/position.ps.struct.reuse.hlsl | 5 +-
.../SemaHLSL/Semantics/target.ps.input.hlsl | 2 +-
clang/test/SemaHLSL/num_threads.hlsl | 7 +-
clang/test/SemaHLSL/shader_type_attr.hlsl | 2 +-
11 files changed, 59 insertions(+), 53 deletions(-)
create mode 100644 clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 0f834df21c40d..dcf165be082a2 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -20,6 +20,7 @@
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/SemaBase.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
@@ -333,7 +334,7 @@ class SemaHLSL : public SemaBase {
void diagnoseSemanticStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage,
llvm::hlsl::IOType CurrentIOType,
- std::initializer_list<llvm::hlsl::SemanticStageInfo> AllowedStages);
+ llvm::ArrayRef<llvm::hlsl::SemanticStageInfo> AllowedStages);
void handleGlobalStructOrArrayOfWithResources(VarDecl *VD);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5ed6d4632d8ee..a678df9e4774b 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1080,15 +1080,19 @@ void SemaHLSL::checkSemanticAnnotation(
assert(ShaderAttr && "Entry point has no shader attribute");
llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
- auto SemanticName = SemanticAttr->getSemanticName().upper();
- if (SemanticName == "SV_DISPATCHTHREADID" ||
- SemanticName == "SV_GROUPINDEX" || SemanticName == "SV_GROUPTHREADID" ||
- SemanticName == "SV_GROUPID") {
-
- if (ST != llvm::Triple::Compute)
- diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
- {{llvm::Triple::Compute, IOType::In}});
+ llvm::dxbc::PSV::SemanticKind SemanticKind =
+ llvm::hlsl::getSemanticKind(SemanticAttr->getSemanticName());
+ llvm::hlsl::SemanticInterpretation Interpretation =
+ llvm::hlsl::getInterpretationKind(SemanticKind, ST, SC.CurrentIOType);
+ if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
+ llvm::hlsl::getAvailableStages(SemanticKind));
+ switch (SemanticKind) {
+ case llvm::dxbc::PSV::SemanticKind::DispatchThreadID:
+ case llvm::dxbc::PSV::SemanticKind::GroupID:
+ case llvm::dxbc::PSV::SemanticKind::GroupIndex:
+ case llvm::dxbc::PSV::SemanticKind::GroupThreadID:
if (SemanticAttr->getSemanticIndex() != 0) {
std::string PrettyName =
"'" + SemanticAttr->getSemanticName().str() + "'";
@@ -1096,33 +1100,10 @@ void SemaHLSL::checkSemanticAnnotation(
diag::err_hlsl_semantic_indexing_not_supported)
<< PrettyName;
}
- return;
- }
-
- if (SemanticName == "SV_POSITION") {
- // SV_Position can be an input or output in vertex shaders,
- // but only an input in pixel shaders.
- diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
- {{llvm::Triple::Vertex, IOType::InOut},
- {llvm::Triple::Pixel, IOType::In}});
- return;
- }
- if (SemanticName == "SV_VERTEXID") {
- diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
- {{llvm::Triple::Vertex, IOType::In}});
- return;
- }
-
- if (SemanticName == "SV_TARGET") {
- diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
- {{llvm::Triple::Pixel, IOType::Out}});
- return;
+ break;
+ default:
+ break;
}
-
- // FIXME: catch-all for non-implemented system semantics reaching this
- // location.
- if (SemanticAttr->getAttrName()->getName().starts_with_insensitive("SV_"))
- llvm_unreachable("Unknown SemanticAttr");
}
void SemaHLSL::diagnoseAttrStageMismatch(
@@ -1141,9 +1122,21 @@ void SemaHLSL::diagnoseAttrStageMismatch(
void SemaHLSL::diagnoseSemanticStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
- std::initializer_list<SemanticStageInfo> Allowed) {
+ ArrayRef<SemanticStageInfo> Allowed) {
+ SmallVector<SemanticStageInfo, 8> CombinedAllowed;
+ for (const SemanticStageInfo &Case : Allowed) {
+ auto It = llvm::find_if(CombinedAllowed, [&](SemanticStageInfo Info) {
+ return Info.Stage == Case.Stage;
+ });
+ if (It == CombinedAllowed.end()) {
+ CombinedAllowed.push_back(Case);
+ continue;
+ }
+ It->AllowedIOTypesMask =
+ static_cast<IOType>(It->AllowedIOTypesMask | Case.AllowedIOTypesMask);
+ }
- for (auto &Case : Allowed) {
+ for (auto &Case : CombinedAllowed) {
if (Case.Stage != Stage)
continue;
@@ -1152,7 +1145,8 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
SmallVector<std::string, 8> ValidCases;
llvm::transform(
- Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) {
+ CombinedAllowed, std::back_inserter(ValidCases),
+ [](SemanticStageInfo Case) {
SmallVector<std::string, 2> ValidType;
if (Case.AllowedIOTypesMask & IOType::In)
ValidType.push_back("input");
@@ -1178,14 +1172,15 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
SmallVector<StringRef, 8> StageStrings;
llvm::transform(
- Allowed, std::back_inserter(StageStrings), [](SemanticStageInfo Case) {
+ CombinedAllowed, std::back_inserter(StageStrings),
+ [](SemanticStageInfo Case) {
return StringRef(
HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage));
});
Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
<< A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
- << (Allowed.size() != 1) << join(StageStrings, ", ");
+ << (CombinedAllowed.size() != 1) << join(StageStrings, ", ");
}
template <CastKind Kind>
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
index 095532863ac5a..b1a0a2b16c3d1 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
@@ -4,10 +4,10 @@
// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
// CHECK: define void @main() {{.*}} {
-float4 main(float4 p : SV_Position) : A {
+float4 main(float4 p : SV_Position) : SV_Target {
// CHECK-SPIRV: %[[P:.*]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 4
// CHECK-SPIRV: %[[R:.*]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[P]])
- // CHECK-SPIRV: store <4 x float> %[[R]], ptr addrspace(8) @A0, align 4
+ // CHECK-SPIRV: store <4 x float> %[[R]], ptr addrspace(8) @SV_Target0, align 4
// CHECK-DXIL: %[[INPUT:.*]] = call <4 x float> @llvm.dx.load.input.v4f32(i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %[[RESULT:.*]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %[[INPUT]])
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
index 5427a569e5eee..8a8ba0f52bc1e 100644
--- a/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
@@ -8,7 +8,7 @@ struct S0 {
// CHECK-SPIRV-DAG: @A0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#METADATA_0:]]
-[shader("pixel")]
+[shader("vertex")]
S0 main1(float4 input : A) : B {
// CHECK: %[[ARG:.*]] = alloca %struct.S0
// CHECK-SPIRV: %[[INPUT:.*]] = load <4 x float>, ptr addrspace(7) @A0, align 4
diff --git a/clang/test/SemaHLSL/Availability/attr-availability-pixel.hlsl b/clang/test/SemaHLSL/Availability/attr-availability-pixel.hlsl
index 83c49738f8810..0f24f7f10f980 100644
--- a/clang/test/SemaHLSL/Availability/attr-availability-pixel.hlsl
+++ b/clang/test/SemaHLSL/Availability/attr-availability-pixel.hlsl
@@ -36,7 +36,7 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = compute
__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
unsigned f8();
-int main() : A {
+float main() : SV_Target {
// expected-error@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
// expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
unsigned A = f1(); // #f1_call
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
new file mode 100644
index 0000000000000..308bb48b096ba
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
+
+float4 main(float4 a : A) : B {
+// expected-error at -1 {{semantic 'B' is unsupported in pixel shaders as output}}
+ return a;
+}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
index 213a53e30155b..d8fdd58ba0855 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
@@ -9,13 +9,12 @@ struct S {
// CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "SV_Position" 3
};
-// FIXME(Keenuts): add mandatory output semantic once those are implemented.
-float4 main(S s) : B {
+float4 main(S s) : SV_Target {
// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 'float4 (S)'
// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:15 used s 'S'
// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:4:15> "SV_Position" 0
// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:7:15> "SV_Position" 3
-// CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:20> "B" 0
+// CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:20> "SV_Target" 0
return s.f1;
}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.struct.reuse.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.struct.reuse.hlsl
index d10c817d53af2..532d73131895b 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.struct.reuse.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.struct.reuse.hlsl
@@ -16,14 +16,13 @@ struct Top {
};
-// FIXME(Keenuts): add mandatory output semantic once those are implemented.
-float4 main(Top s : D) : F4 {
+float4 main(Top s : D) : SV_Target {
// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 'float4 (Top)'
// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:17 used s 'Top'
// CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:21> "D" 0
// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:21> "D" 0
// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:21> "D" 1
-// CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:26> "F" 4
+// CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:26> "SV_Target" 0
return s.f0.x;
}
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index a77b46c0e9f1a..87972640e294a 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -finclude-default-header -x hlsl -verify -o - %s
// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
-float4 main(float4 a : SV_Target) : A {
+float4 main(float4 a : SV_Target) : SV_Target {
// expected-error at -1 {{semantic 'SV_Target' is unsupported in pixel shaders as input, requires one of the following: pixel out}}
return a;
}
diff --git a/clang/test/SemaHLSL/num_threads.hlsl b/clang/test/SemaHLSL/num_threads.hlsl
index 52e71ec458161..6713af80ba9d6 100644
--- a/clang/test/SemaHLSL/num_threads.hlsl
+++ b/clang/test/SemaHLSL/num_threads.hlsl
@@ -130,7 +130,12 @@ int largeZ();
#endif
// expected-error-re at +1 {{attribute 'numthreads' is unsupported in '{{[A-Za-z]+}}' shaders, requires one of the following: compute, amplification, mesh}}
[numthreads(1,1,1)]
-int main() : A {
+#if __SHADER_TARGET_STAGE == __SHADER_STAGE_PIXEL
+float main() : SV_Target
+#else
+int main() : A
+#endif
+{
return 1;
}
diff --git a/clang/test/SemaHLSL/shader_type_attr.hlsl b/clang/test/SemaHLSL/shader_type_attr.hlsl
index 5f30a520b7255..c0c3ec4fe0e6d 100644
--- a/clang/test/SemaHLSL/shader_type_attr.hlsl
+++ b/clang/test/SemaHLSL/shader_type_attr.hlsl
@@ -31,7 +31,7 @@ static void oops() {}
[shader("pixel")]
// expected-note at +1 {{conflicting attribute is here}}
[shader("vertex")]
-int doubledUp() : A {
+float doubledUp() : SV_Target {
return 1;
}
>From 0c4a469003efa74ed82544bb96db8898b2fc75d6 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 18:21:46 +0000
Subject: [PATCH 17/28] nfc: refactor cghlsl to not do logic on the names
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 132 ++++++++++++++--------------
clang/lib/CodeGen/CGHLSLRuntime.h | 12 +--
2 files changed, 74 insertions(+), 70 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 05f755edde64b..66178493a9ccc 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1435,18 +1435,17 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
}
llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
- IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
- const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index, SemanticSignatures &Signature) {
-
- std::string SemanticName = Semantic->getAttrName()->getName().upper();
- if (SemanticName == "SV_GROUPINDEX") {
+ IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic,
+ llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+ SemanticSignatures &Signature) {
+ switch (SemanticKind) {
+ case llvm::dxbc::PSV::SemanticKind::GroupIndex: {
llvm::Function *GroupIndex =
CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic());
return B.CreateCall(FunctionCallee(GroupIndex));
}
-
- if (SemanticName == "SV_DISPATCHTHREADID") {
+ case llvm::dxbc::PSV::SemanticKind::DispatchThreadID: {
llvm::Intrinsic::ID IntrinID = getThreadIdIntrinsic();
llvm::Function *ThreadIDIntrinsic =
llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1454,8 +1453,7 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
: CGM.getIntrinsic(IntrinID);
return buildVectorInput(B, ThreadIDIntrinsic, Type);
}
-
- if (SemanticName == "SV_GROUPTHREADID") {
+ case llvm::dxbc::PSV::SemanticKind::GroupThreadID: {
llvm::Intrinsic::ID IntrinID = getGroupThreadIdIntrinsic();
llvm::Function *GroupThreadIDIntrinsic =
llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1463,8 +1461,7 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
: CGM.getIntrinsic(IntrinID);
return buildVectorInput(B, GroupThreadIDIntrinsic, Type);
}
-
- if (SemanticName == "SV_GROUPID") {
+ case llvm::dxbc::PSV::SemanticKind::GroupID: {
llvm::Intrinsic::ID IntrinID = getGroupIdIntrinsic();
llvm::Function *GroupIDIntrinsic =
llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1472,38 +1469,26 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
: CGM.getIntrinsic(IntrinID);
return buildVectorInput(B, GroupIDIntrinsic, Type);
}
-
- const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
- assert(ShaderAttr && "Entry point has no shader attribute");
- llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
-
- if (SemanticName == "SV_POSITION") {
- if (ST == Triple::EnvironmentType::Pixel) {
- if (CGM.getTarget().getTriple().isSPIRV())
- return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
- Semantic->getAttrName()->getName(),
- /* BuiltIn::FragCoord */ 15);
- if (CGM.getTarget().getTriple().isDXIL())
- return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
- Signature);
- }
-
- if (ST == Triple::EnvironmentType::Vertex) {
- return emitUserSemanticLoad(B, FD, Type, Decl, Semantic, Index,
- Signature);
- }
- }
-
- if (SemanticName == "SV_VERTEXID") {
- if (ST == Triple::EnvironmentType::Vertex) {
- if (CGM.getTarget().getTriple().isSPIRV())
- return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
- Semantic->getAttrName()->getName(),
- /* BuiltIn::VertexIndex */ 42);
- else
- return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
- Signature);
- }
+ case llvm::dxbc::PSV::SemanticKind::Position:
+ if (CGM.getTarget().getTriple().isSPIRV())
+ return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
+ Semantic->getAttrName()->getName(),
+ /* BuiltIn::FragCoord */ 15);
+ if (CGM.getTarget().getTriple().isDXIL())
+ return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
+ Signature);
+ break;
+ case llvm::dxbc::PSV::SemanticKind::VertexID:
+ if (CGM.getTarget().getTriple().isSPIRV())
+ return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
+ Semantic->getAttrName()->getName(),
+ /* BuiltIn::VertexIndex */ 42);
+ if (CGM.getTarget().getTriple().isDXIL())
+ return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
+ Signature);
+ break;
+ default:
+ break;
}
llvm_unreachable(
@@ -1524,30 +1509,29 @@ static void createSPIRVBuiltinStore(IRBuilder<> &B, llvm::Module &M,
B.CreateStore(Source, GV);
}
-void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
- const clang::DeclaratorDecl *Decl,
- HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index,
- SemanticSignatures &Signature) {
-
- std::string SemanticName = Semantic->getAttrName()->getName().upper();
- if (SemanticName == "SV_POSITION") {
+void CGHLSLRuntime::emitSystemSemanticStore(
+ IRBuilder<> &B, llvm::Value *Source, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic,
+ llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+ SemanticSignatures &Signature) {
+ switch (SemanticKind) {
+ case llvm::dxbc::PSV::SemanticKind::Position:
if (CGM.getTarget().getTriple().isDXIL()) {
emitDXILUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
return;
}
-
if (CGM.getTarget().getTriple().isSPIRV()) {
createSPIRVBuiltinStore(B, CGM.getModule(), Source,
Semantic->getAttrName()->getName(),
/* BuiltIn::Position */ 0);
return;
}
- }
-
- if (SemanticName == "SV_TARGET") {
+ break;
+ case llvm::dxbc::PSV::SemanticKind::Target:
emitUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
return;
+ default:
+ break;
}
llvm_unreachable(
@@ -1560,10 +1544,19 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
SemanticSignatures &Signature) {
std::optional<unsigned> Index = Semantic->getSemanticIndex();
- if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_"))
- return emitSystemSemanticLoad(B, FD, Type, Decl, Semantic, Index,
- Signature);
- return emitUserSemanticLoad(B, FD, Type, Decl, Semantic, Index, Signature);
+ llvm::dxbc::PSV::SemanticKind SemanticKind =
+ llvm::hlsl::getSemanticKind(Semantic->getAttrName()->getName());
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+ llvm::hlsl::SemanticInterpretation Interpretation =
+ llvm::hlsl::getInterpretationKind(SemanticKind, ShaderAttr->getType(),
+ llvm::hlsl::IOType::In);
+ if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
+ llvm_unreachable("invalid semantic should have been diagnosed by Sema");
+ if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary)
+ return emitUserSemanticLoad(B, FD, Type, Decl, Semantic, Index, Signature);
+ return emitSystemSemanticLoad(B, Type, Decl, Semantic, SemanticKind, Index,
+ Signature);
}
void CGHLSLRuntime::handleScalarSemanticStore(IRBuilder<> &B,
@@ -1573,10 +1566,21 @@ void CGHLSLRuntime::handleScalarSemanticStore(IRBuilder<> &B,
HLSLAppliedSemanticAttr *Semantic,
SemanticSignatures &Signature) {
std::optional<unsigned> Index = Semantic->getSemanticIndex();
- if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_"))
- emitSystemSemanticStore(B, Source, Decl, Semantic, Index, Signature);
- else
- emitUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
+ llvm::dxbc::PSV::SemanticKind SemanticKind =
+ llvm::hlsl::getSemanticKind(Semantic->getAttrName()->getName());
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+
+ llvm::hlsl::SemanticInterpretation Interpretation =
+ llvm::hlsl::getInterpretationKind(SemanticKind, ShaderAttr->getType(),
+ llvm::hlsl::IOType::Out);
+ assert(Interpretation != llvm::hlsl::SemanticInterpretation::Invalid &&
+ "invalid semantic should have been diagnosed by Sema");
+
+ if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary)
+ return emitUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
+ emitSystemSemanticStore(B, Source, Decl, Semantic, SemanticKind, Index,
+ Signature);
}
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 2f251505cfa3e..29d085b2c76e0 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -222,16 +222,16 @@ class CGHLSLRuntime {
CodeGenModule &CGM;
- llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B,
- const FunctionDecl *FD, llvm::Type *Type,
- const clang::DeclaratorDecl *Decl,
- HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index,
- SemanticSignatures &Signature);
+ llvm::Value *emitSystemSemanticLoad(
+ llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic,
+ llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+ SemanticSignatures &Signature);
void emitSystemSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
+ llvm::dxbc::PSV::SemanticKind SemanticKind,
std::optional<unsigned> Index,
SemanticSignatures &Signature);
>From 73a666ce1a12e2d2e4a46d2430404a628b7f34a0 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 19:00:44 +0000
Subject: [PATCH 18/28] 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 82a3418db7fa040579a50c99749dfceec5bf1a1e 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 19/28] 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 75c7969edafc78b35695a63592d9d2a1adebb7e6 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 20/28] 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 ae2d3c3faaded0a1c5f7d58bb010d3199e6057b6 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 21/28] 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 2358932c90d125608029b175ade924d58383d56c 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 22/28] 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 3dcda307785d7bdb4df57dc85e83eb6b2b85f675 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 23/28] 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 57cd7cb6c41f175ef506f4b3f564254822cc1a42 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:13:01 +0000
Subject: [PATCH 24/28] add a stub indexed signature packing API
---
.../llvm/Frontend/HLSL/SemanticSignaturePacking.h | 11 +++++++++++
llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp | 6 ++++++
2 files changed, 17 insertions(+)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index 14cd477994d68..dacbedbd02d6e 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -57,6 +57,17 @@ LLVM_ABI Error
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
+/// Packs each eligible element at the row denoted by its semantic index and at
+/// column zero. Declaration order does not affect placement, and gaps between
+/// semantic indices remain unused. An element is left unallocated if it is not
+/// part of the signature.
+///
+/// Returns a SignaturePackingError that denotes the first element that cannot
+/// be placed, or success if all eligible elements were placed.
+LLVM_ABI Error
+packSignatureIndexed(MutableArrayRef<SemanticSignatureElement> Elements,
+ Triple::EnvironmentType ShaderStage, IOType IOTy);
+
} // namespace llvm::hlsl
#endif // LLVM_FRONTEND_HLSL_SEMANTICSIGNATUREPACKING_H
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 59289497dce2b..2c17809e1c4f5 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -61,3 +61,9 @@ Error llvm::hlsl::packSignatureStacked(
return Error::success();
}
+
+Error llvm::hlsl::packSignatureIndexed(
+ MutableArrayRef<SemanticSignatureElement>, Triple::EnvironmentType,
+ IOType) {
+ return Error::success();
+}
>From 6556437921616a38d2a727d1e643f41209950886 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:14:13 +0000
Subject: [PATCH 25/28] add indexed signature packing tests
---
.../HLSLSemanticSignaturePackingTest.cpp | 126 ++++++++++++++++--
1 file changed, 118 insertions(+), 8 deletions(-)
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index a1fd495638613..210a4763d4db4 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -27,6 +27,7 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
uint8_t Cols;
dxil::ElementType CompType;
dxbc::PSV::InterpolationMode InterpMode;
+ uint32_t SemanticIndex = 0;
};
struct ExpectedLocation {
@@ -53,7 +54,7 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
for (const ElementConfig &Element : Config.Elements) {
SmallVector<uint32_t> SemanticIndices;
for (uint32_t Row = 0; Row != Element.Rows; ++Row)
- SemanticIndices.push_back(Row);
+ SemanticIndices.push_back(Element.SemanticIndex + Row);
Elements.emplace_back(
/*SigId=*/static_cast<uint32_t>(Elements.size()),
@@ -72,12 +73,20 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
}
- void expectPacking(const TestConfig &Config, unsigned ExpectedRows,
- std::initializer_list<ExpectedLocation> Locations) {
+ Error packIndexed(SmallVectorImpl<SemanticSignatureElement> &Elements,
+ const TestConfig &Config) {
+ return packSignatureIndexed(Elements, Config.ShaderStage, Config.IOTy);
+ }
+
+ void expectPackingImpl(const TestConfig &Config, unsigned ExpectedRows,
+ std::initializer_list<ExpectedLocation> Locations,
+ bool Indexed) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
ASSERT_EQ(Elements.size(), Locations.size());
- ASSERT_THAT_ERROR(packStacked(Elements, Config), Succeeded());
+ Error E =
+ Indexed ? packIndexed(Elements, Config) : packStacked(Elements, Config);
+ ASSERT_THAT_ERROR(std::move(E), Succeeded());
unsigned Rows = 0;
for (const SemanticSignatureElement &Element : Elements)
@@ -93,11 +102,22 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
}
}
- void expectPackingError(const TestConfig &Config,
- SignaturePackingError::ErrorKind ExpectedKind,
- unsigned ExpectedElementIndex) {
+ void expectPacking(const TestConfig &Config, unsigned ExpectedRows,
+ std::initializer_list<ExpectedLocation> Locations) {
+ expectPackingImpl(Config, ExpectedRows, Locations, /*Indexed=*/false);
+ }
+
+ void expectIndexedPacking(const TestConfig &Config, unsigned ExpectedRows,
+ std::initializer_list<ExpectedLocation> Locations) {
+ expectPackingImpl(Config, ExpectedRows, Locations, /*Indexed=*/true);
+ }
+
+ void expectPackingErrorImpl(const TestConfig &Config,
+ SignaturePackingError::ErrorKind ExpectedKind,
+ unsigned ExpectedElementIndex, bool Indexed) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
- Error E = packStacked(Elements, Config);
+ Error E =
+ Indexed ? packIndexed(Elements, Config) : packStacked(Elements, Config);
if (!E) {
ADD_FAILURE() << "expected a SignaturePackingError";
return;
@@ -108,6 +128,20 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
});
}
+
+ void expectPackingError(const TestConfig &Config,
+ SignaturePackingError::ErrorKind ExpectedKind,
+ unsigned ExpectedElementIndex) {
+ expectPackingErrorImpl(Config, ExpectedKind, ExpectedElementIndex,
+ /*Indexed=*/false);
+ }
+
+ void expectIndexedPackingError(const TestConfig &Config,
+ SignaturePackingError::ErrorKind ExpectedKind,
+ unsigned ExpectedElementIndex) {
+ expectPackingErrorImpl(Config, ExpectedKind, ExpectedElementIndex,
+ /*Indexed=*/true);
+ }
};
TEST_F(HLSLSemanticSignaturePackingTest, CreatesSignatureFromConfig) {
@@ -339,4 +373,80 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsMultiRowSignatureOverflow) {
/*ExpectedElementIndex=*/1);
}
+//===----------------------------------------------------------------------===//
+// Indexed packing tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(HLSLSemanticSignaturePackingTest, IndexedUsesSemanticIndices) {
+ // Target elements are assigned the row denoted by their semantic index, not
+ // their declaration order. Every target starts at column zero.
+
+ // struct PSOut {
+ // float4 Color3 : SV_Target3;
+ // float Color0 : SV_Target0;
+ // float2 Color2 : SV_Target2;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Pixel, IOType::Out,
+ {{dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/4,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/3},
+ {dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/1,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/0},
+ {dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/2,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/2}});
+
+ // Expected layout:
+ // reg0: Color0.x | unused.yzw
+ // reg1: unused.xyzw
+ // reg2: Color2.xy | unused.zw
+ // reg3: Color3.xyzw
+ expectIndexedPacking(
+ Config, /*ExpectedRows=*/4,
+ {{/*Row=*/3, /*Col=*/0}, {/*Row=*/0, /*Col=*/0}, {/*Row=*/2, /*Col=*/0}});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, IndexedLeavesSemanticIndexGaps) {
+ // Rows without a corresponding target semantic remain unused.
+
+ // struct PSOut {
+ // float4 Color1 : SV_Target1;
+ // float4 Color7 : SV_Target7;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Pixel, IOType::Out,
+ {{dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/4,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/1},
+ {dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/4,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/7}});
+
+ // Expected layout:
+ // reg0: unused.xyzw
+ // reg1: Color1.xyzw
+ // reg2-6: unused.xyzw
+ // reg7: Color7.xyzw
+ expectIndexedPacking(Config, /*ExpectedRows=*/8,
+ {{/*Row=*/1, /*Col=*/0}, {/*Row=*/7, /*Col=*/0}});
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest, IndexedRejectsSemanticIndexOverflow) {
+ // A semantic index outside the 32-row signature cannot be allocated.
+
+ // struct PSOut {
+ // float4 Color32 : SV_Target32;
+ // };
+ TestConfig Config(
+ Triple::EnvironmentType::Pixel, IOType::Out,
+ {{dxbc::PSV::SemanticKind::Target, /*Rows=*/1, /*Cols=*/4,
+ dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
+ /*SemanticIndex=*/MaxSignatureRows}});
+
+ expectIndexedPackingError(Config, SignaturePackingError::SignatureOverflow,
+ /*ExpectedElementIndex=*/0);
+}
+
} // namespace
>From 36b3b9d7963c5aaf5a00c3a15790f1a7f6b30352 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:16:27 +0000
Subject: [PATCH 26/28] implement indexed signature packing
---
.../HLSL/SemanticSignaturePacking.cpp | 31 +++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
index 2c17809e1c4f5..5b25584f33539 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignaturePacking.cpp
@@ -63,7 +63,34 @@ Error llvm::hlsl::packSignatureStacked(
}
Error llvm::hlsl::packSignatureIndexed(
- MutableArrayRef<SemanticSignatureElement>, Triple::EnvironmentType,
- IOType) {
+ MutableArrayRef<SemanticSignatureElement> Elements,
+ Triple::EnvironmentType ShaderStage, IOType IOTy) {
+ 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::Target &&
+ "unexpected semantic interpretation for indexed packing");
+ assert(Element.Rows == 1 && Element.SemanticIndices.size() == 1 &&
+ "target elements must occupy one semantic row");
+
+ const uint32_t Row = Element.SemanticIndices.front();
+ if (Row >= MaxSignatureRows)
+ return make_error<SignaturePackingError>(
+ SignaturePackingError::SignatureOverflow,
+ static_cast<unsigned>(Index));
+
+ Element.StartRow = Row;
+ Element.StartCol = 0;
+ }
+
return Error::success();
}
>From 030ba4bdb9ae3cd98c122fc744ea0316af127ae6 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:21:45 +0000
Subject: [PATCH 27/28] document indexed signature packing
---
llvm/docs/DirectX/SemanticSignatures.md | 27 +++++++++++++++++++
.../Frontend/HLSL/SemanticSignaturePacking.h | 8 ++----
2 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/llvm/docs/DirectX/SemanticSignatures.md b/llvm/docs/DirectX/SemanticSignatures.md
index 985a6d596a9e5..56f78a7e4599c 100644
--- a/llvm/docs/DirectX/SemanticSignatures.md
+++ b/llvm/docs/DirectX/SemanticSignatures.md
@@ -173,3 +173,30 @@ reg1: B[0].xyz | unused.w
reg2: B[1].xyz | unused.w
reg3: VertexID.x | unused.yzw
```
+
+### Indexed Packing
+
+Indexed packing is used for a pixel shader output signature. Each eligible
+`SV_Target` element occupies one row and starts at column zero. Its semantic
+index directly selects that row, so declaration order does not affect placement
+and rows without a corresponding semantic index remain unused. Elements that do
+not contribute to the target register space remain unallocated.
+
+For example:
+
+```hlsl
+struct PSOut {
+ float4 Color3 : SV_Target3;
+ float Color0 : SV_Target0;
+ float2 Color2 : SV_Target2;
+};
+```
+
+The signature is allocated as:
+
+```text
+reg0: Color0.x | unused.yzw
+reg1: unused.xyzw
+reg2: Color2.xy | unused.zw
+reg3: Color3.xyzw
+```
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
index dacbedbd02d6e..07be894251a65 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignaturePacking.h
@@ -57,13 +57,9 @@ LLVM_ABI Error
packSignatureStacked(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
-/// Packs each eligible element at the row denoted by its semantic index and at
-/// column zero. Declaration order does not affect placement, and gaps between
-/// semantic indices remain unused. An element is left unallocated if it is not
-/// part of the signature.
+/// Packs eligible signature elements at rows selected by semantic index.
///
-/// 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#indexed-packing for details.
LLVM_ABI Error
packSignatureIndexed(MutableArrayRef<SemanticSignatureElement> Elements,
Triple::EnvironmentType ShaderStage, IOType IOTy);
>From b8f2ae645f21270e0718da03df356a90abfb752d Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 20:42:32 +0000
Subject: [PATCH 28/28] simplify signature packing test harness
---
.../HLSLSemanticSignaturePackingTest.cpp | 100 ++++++++----------
1 file changed, 42 insertions(+), 58 deletions(-)
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index 210a4763d4db4..027d6c267531a 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -48,6 +48,11 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
: ShaderStage(ShaderStage), IOTy(IOTy), Elements(Elements) {}
};
+ enum class PackingMethod {
+ Stacked,
+ Indexed,
+ };
+
SmallVector<SemanticSignatureElement>
makeSignature(const TestConfig &Config) {
SmallVector<SemanticSignatureElement> Elements;
@@ -68,25 +73,25 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
return Elements;
}
- Error packStacked(SmallVectorImpl<SemanticSignatureElement> &Elements,
- const TestConfig &Config) {
- return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
- }
-
- Error packIndexed(SmallVectorImpl<SemanticSignatureElement> &Elements,
- const TestConfig &Config) {
- return packSignatureIndexed(Elements, Config.ShaderStage, Config.IOTy);
+ Error pack(PackingMethod Method,
+ SmallVectorImpl<SemanticSignatureElement> &Elements,
+ const TestConfig &Config) {
+ switch (Method) {
+ case PackingMethod::Stacked:
+ return packSignatureStacked(Elements, Config.ShaderStage, Config.IOTy);
+ case PackingMethod::Indexed:
+ return packSignatureIndexed(Elements, Config.ShaderStage, Config.IOTy);
+ }
+ llvm_unreachable("invalid packing method");
}
- void expectPackingImpl(const TestConfig &Config, unsigned ExpectedRows,
- std::initializer_list<ExpectedLocation> Locations,
- bool Indexed) {
+ void expectPacking(PackingMethod Method, const TestConfig &Config,
+ unsigned ExpectedRows,
+ std::initializer_list<ExpectedLocation> Locations) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
ASSERT_EQ(Elements.size(), Locations.size());
- Error E =
- Indexed ? packIndexed(Elements, Config) : packStacked(Elements, Config);
- ASSERT_THAT_ERROR(std::move(E), Succeeded());
+ ASSERT_THAT_ERROR(pack(Method, Elements, Config), Succeeded());
unsigned Rows = 0;
for (const SemanticSignatureElement &Element : Elements)
@@ -102,22 +107,11 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
}
}
- void expectPacking(const TestConfig &Config, unsigned ExpectedRows,
- std::initializer_list<ExpectedLocation> Locations) {
- expectPackingImpl(Config, ExpectedRows, Locations, /*Indexed=*/false);
- }
-
- void expectIndexedPacking(const TestConfig &Config, unsigned ExpectedRows,
- std::initializer_list<ExpectedLocation> Locations) {
- expectPackingImpl(Config, ExpectedRows, Locations, /*Indexed=*/true);
- }
-
- void expectPackingErrorImpl(const TestConfig &Config,
- SignaturePackingError::ErrorKind ExpectedKind,
- unsigned ExpectedElementIndex, bool Indexed) {
+ void expectPackingError(PackingMethod Method, const TestConfig &Config,
+ SignaturePackingError::ErrorKind ExpectedKind,
+ unsigned ExpectedElementIndex) {
SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
- Error E =
- Indexed ? packIndexed(Elements, Config) : packStacked(Elements, Config);
+ Error E = pack(Method, Elements, Config);
if (!E) {
ADD_FAILURE() << "expected a SignaturePackingError";
return;
@@ -128,20 +122,6 @@ class HLSLSemanticSignaturePackingTest : public testing::Test {
EXPECT_EQ(PackingErr.getElementIndex(), ExpectedElementIndex);
});
}
-
- void expectPackingError(const TestConfig &Config,
- SignaturePackingError::ErrorKind ExpectedKind,
- unsigned ExpectedElementIndex) {
- expectPackingErrorImpl(Config, ExpectedKind, ExpectedElementIndex,
- /*Indexed=*/false);
- }
-
- void expectIndexedPackingError(const TestConfig &Config,
- SignaturePackingError::ErrorKind ExpectedKind,
- unsigned ExpectedElementIndex) {
- expectPackingErrorImpl(Config, ExpectedKind, ExpectedElementIndex,
- /*Indexed=*/true);
- }
};
TEST_F(HLSLSemanticSignaturePackingTest, CreatesSignatureFromConfig) {
@@ -204,7 +184,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, SkipsNotAllocatedElements) {
dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Undefined}});
// Expected layout: no registers are used.
- expectPacking(Config, /*ExpectedRows=*/0,
+ expectPacking(PackingMethod::Stacked, Config, /*ExpectedRows=*/0,
{Unallocated, Unallocated, Unallocated});
}
@@ -231,7 +211,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, StacksInDeclarationOrder) {
// reg1: Data.xy | unused.zw
// reg2: ClipDistance.xyz | unused.w
expectPacking(
- Config, /*ExpectedRows=*/3,
+ PackingMethod::Stacked, Config, /*ExpectedRows=*/3,
{{/*Row=*/0, /*Col=*/0}, {/*Row=*/1, /*Col=*/0}, {/*Row=*/2, /*Col=*/0}});
}
@@ -260,7 +240,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, DoesNotCoPackElements) {
// reg1: B.x | unused.yzw
// reg2: C.x | unused.yzw
// reg3: D.x | unused.yzw
- expectPacking(Config, /*ExpectedRows=*/4,
+ expectPacking(PackingMethod::Stacked, Config, /*ExpectedRows=*/4,
{{/*Row=*/0, /*Col=*/0},
{/*Row=*/1, /*Col=*/0},
{/*Row=*/2, /*Col=*/0},
@@ -292,7 +272,7 @@ TEST_F(HLSLSemanticSignaturePackingTest, StacksMultiRowElements) {
// reg4: B[1].xyz | unused.w
// reg5: C.xyzw
expectPacking(
- Config, /*ExpectedRows=*/6,
+ PackingMethod::Stacked, Config, /*ExpectedRows=*/6,
{{/*Row=*/0, /*Col=*/0}, {/*Row=*/3, /*Col=*/0}, {/*Row=*/5, /*Col=*/0}});
}
@@ -310,8 +290,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, ExactlyFillsSignature) {
// Expected layout:
// reg0-31: A[0-31].xyzw
- expectPacking(Config, /*ExpectedRows=*/MaxSignatureRows,
- {{/*Row=*/0, /*Col=*/0}});
+ expectPacking(PackingMethod::Stacked, Config,
+ /*ExpectedRows=*/MaxSignatureRows, {{/*Row=*/0, /*Col=*/0}});
}
//===----------------------------------------------------------------------===//
@@ -334,7 +314,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsSignatureOverflow) {
dxbc::PSV::InterpolationMode::Linear});
// The last element is the one that no longer fits.
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ expectPackingError(PackingMethod::Stacked, Config,
+ SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/MaxSignatureRows);
}
@@ -350,7 +331,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsSingleElementOverflow) {
/*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
dxbc::PSV::InterpolationMode::Linear}});
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ expectPackingError(PackingMethod::Stacked, Config,
+ SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/0);
}
@@ -369,7 +351,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, RejectsMultiRowSignatureOverflow) {
/*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
dxbc::PSV::InterpolationMode::Linear}});
- expectPackingError(Config, SignaturePackingError::SignatureOverflow,
+ expectPackingError(PackingMethod::Stacked, Config,
+ SignaturePackingError::SignatureOverflow,
/*ExpectedElementIndex=*/1);
}
@@ -403,8 +386,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, IndexedUsesSemanticIndices) {
// reg1: unused.xyzw
// reg2: Color2.xy | unused.zw
// reg3: Color3.xyzw
- expectIndexedPacking(
- Config, /*ExpectedRows=*/4,
+ expectPacking(
+ PackingMethod::Indexed, Config, /*ExpectedRows=*/4,
{{/*Row=*/3, /*Col=*/0}, {/*Row=*/0, /*Col=*/0}, {/*Row=*/2, /*Col=*/0}});
}
@@ -429,8 +412,8 @@ TEST_F(HLSLSemanticSignaturePackingTest, IndexedLeavesSemanticIndexGaps) {
// reg1: Color1.xyzw
// reg2-6: unused.xyzw
// reg7: Color7.xyzw
- expectIndexedPacking(Config, /*ExpectedRows=*/8,
- {{/*Row=*/1, /*Col=*/0}, {/*Row=*/7, /*Col=*/0}});
+ expectPacking(PackingMethod::Indexed, Config, /*ExpectedRows=*/8,
+ {{/*Row=*/1, /*Col=*/0}, {/*Row=*/7, /*Col=*/0}});
}
TEST_F(HLSLSemanticSignaturePackingTest, IndexedRejectsSemanticIndexOverflow) {
@@ -445,8 +428,9 @@ TEST_F(HLSLSemanticSignaturePackingTest, IndexedRejectsSemanticIndexOverflow) {
dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Undefined,
/*SemanticIndex=*/MaxSignatureRows}});
- expectIndexedPackingError(Config, SignaturePackingError::SignatureOverflow,
- /*ExpectedElementIndex=*/0);
+ expectPackingError(PackingMethod::Indexed, Config,
+ SignaturePackingError::SignatureOverflow,
+ /*ExpectedElementIndex=*/0);
}
} // namespace
More information about the cfe-commits
mailing list