[clang] [llvm] [NFC][SemaHLSL][CGHLSL] Move semantic classification to common `HLSL/SemanticSignatures` (PR #218058)

Finn Plummer via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 9 14:49:41 PDT 2026


https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/218058

>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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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/36] 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 0ce7889dda3d439120f49528180ab44faafef134 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Fri, 21 Aug 2026 23:23:06 +0000
Subject: [PATCH 19/36] audit all uses in SemaHLSL

---
 clang/include/clang/Sema/SemaHLSL.h |   1 +
 clang/lib/Sema/SemaHLSL.cpp         | 118 ++++++++++------------------
 2 files changed, 41 insertions(+), 78 deletions(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index dcf165be082a2..9e2212a6d9eda 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -207,6 +207,7 @@ class SemaHLSL : public SemaBase {
   }
 
   void diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
+                                  llvm::dxbc::PSV::SemanticKind SemanticKind,
                                   std::optional<unsigned> Index);
   void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a678df9e4774b..5e075809101fd 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -56,6 +56,7 @@ using namespace clang;
 using namespace clang::hlsl;
 using llvm::hlsl::IOType;
 using llvm::hlsl::SemanticStageInfo;
+using SemanticKind = llvm::dxbc::PSV::SemanticKind;
 using RegisterType = HLSLResourceBindingAttr::RegisterType;
 
 static CXXRecordDecl *createHostLayoutStruct(Sema &S,
@@ -873,19 +874,20 @@ static bool isVkPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
   const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
   assert(ShaderAttr && "Entry point has no shader attribute");
   llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
-  auto SemanticName = Semantic->getSemanticName().upper();
+  SemanticKind Kind = llvm::hlsl::getSemanticKind(Semantic->getSemanticName());
 
-  // The SV_Position semantic is lowered to:
-  //  - Position built-in for vertex output.
-  //  - FragCoord built-in for fragment input.
-  if (SemanticName == "SV_POSITION") {
+  switch (Kind) {
+  case SemanticKind::Position:
+    // The SV_Position semantic is lowered to:
+    //  - Position built-in for vertex output.
+    //  - FragCoord built-in for fragment input.
     return (ST == llvm::Triple::Vertex && !IsInput) ||
            (ST == llvm::Triple::Pixel && IsInput);
-  }
-  if (SemanticName == "SV_VERTEXID")
+  case SemanticKind::VertexID:
     return true;
-
-  return false;
+  default:
+    return false;
+  }
 }
 
 bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
@@ -1080,19 +1082,19 @@ void SemaHLSL::checkSemanticAnnotation(
   assert(ShaderAttr && "Entry point has no shader attribute");
   llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
 
-  llvm::dxbc::PSV::SemanticKind SemanticKind =
+  SemanticKind Kind =
       llvm::hlsl::getSemanticKind(SemanticAttr->getSemanticName());
   llvm::hlsl::SemanticInterpretation Interpretation =
-      llvm::hlsl::getInterpretationKind(SemanticKind, ST, SC.CurrentIOType);
+      llvm::hlsl::getInterpretationKind(Kind, ST, SC.CurrentIOType);
   if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
     diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
-                                  llvm::hlsl::getAvailableStages(SemanticKind));
+                                  llvm::hlsl::getAvailableStages(Kind));
 
-  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:
+  switch (Kind) {
+  case SemanticKind::DispatchThreadID:
+  case SemanticKind::GroupID:
+  case SemanticKind::GroupIndex:
+  case SemanticKind::GroupThreadID:
     if (SemanticAttr->getSemanticIndex() != 0) {
       std::string PrettyName =
           "'" + SemanticAttr->getSemanticName().str() + "'";
@@ -1927,9 +1929,8 @@ bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) {
 }
 
 void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
+                                          SemanticKind Kind,
                                           std::optional<unsigned> Index) {
-  std::string SemanticName = AL.getAttrName()->getName().upper();
-
   auto *VD = cast<ValueDecl>(D);
   QualType ValueType = VD->getType();
   if (auto *FD = dyn_cast<FunctionDecl>(D))
@@ -1943,74 +1944,34 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
     }
   }
 
-  if (SemanticName == "SV_DISPATCHTHREADID") {
-    diagnoseInputIDType(ValueType, AL);
-    if (IsOutput)
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
-    if (Index.has_value())
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << AL;
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
-  }
-
-  if (SemanticName == "SV_GROUPINDEX") {
-    if (IsOutput)
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
-    if (Index.has_value())
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << AL;
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
-  }
-
-  if (SemanticName == "SV_GROUPTHREADID") {
-    diagnoseInputIDType(ValueType, AL);
-    if (IsOutput)
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
-    if (Index.has_value())
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << AL;
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
-  }
-
-  if (SemanticName == "SV_GROUPID") {
+  switch (Kind) {
+  case SemanticKind::DispatchThreadID:
+  case SemanticKind::GroupThreadID:
+  case SemanticKind::GroupID:
     diagnoseInputIDType(ValueType, AL);
+    [[fallthrough]];
+  case SemanticKind::GroupIndex:
     if (IsOutput)
       Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
     if (Index.has_value())
       Diag(AL.getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << AL;
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
-  }
-
-  if (SemanticName == "SV_POSITION") {
-    const auto *VT = ValueType->getAs<VectorType>();
-    if (!ValueType->hasFloatingRepresentation() ||
-        (VT && VT->getNumElements() > 4))
-      Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
-          << AL << "float/float1/float2/float3/float4";
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
-  }
-
-  if (SemanticName == "SV_VERTEXID") {
+    break;
+  case SemanticKind::Position:
+  case SemanticKind::Target:
+    diagnosePositionType(ValueType, AL);
+    break;
+  case SemanticKind::VertexID: {
     uint64_t SizeInBits = SemaRef.Context.getTypeSize(ValueType);
     if (!ValueType->isUnsignedIntegerType() || SizeInBits != 32)
       Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) << AL << "uint";
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-    return;
+    break;
   }
-
-  if (SemanticName == "SV_TARGET") {
-    const auto *VT = ValueType->getAs<VectorType>();
-    if (!ValueType->hasFloatingRepresentation() ||
-        (VT && VT->getNumElements() > 4))
-      Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
-          << AL << "float/float1/float2/float3/float4";
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+  default:
+    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
     return;
   }
 
-  Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
+  D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
 }
 
 void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
@@ -2023,10 +1984,11 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
   std::optional<unsigned> Index =
       ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt;
 
-  if (AL.getAttrName()->getName().starts_with_insensitive("SV_"))
-    diagnoseSystemSemanticAttr(D, AL, Index);
-  else
+  SemanticKind Kind = llvm::hlsl::getSemanticKind(AL.getAttrName()->getName());
+  if (Kind == SemanticKind::Arbitrary)
     D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+  else
+    diagnoseSystemSemanticAttr(D, AL, Kind, Index);
 }
 
 void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {

>From 5f5aab245321be0eec55e083b92ac95f512eace0 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 16:27:18 +0000
Subject: [PATCH 20/36] review: clean up nits

---
 clang/lib/Sema/SemaHLSL.cpp                          | 6 +++---
 llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 5 +++--
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp        | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index da958578a418e..9341c0a3c9fdd 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1127,7 +1127,7 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
     ArrayRef<SemanticStageInfo> Allowed) {
   SmallVector<SemanticStageInfo, 8> CombinedAllowed;
   for (const SemanticStageInfo &Case : Allowed) {
-    auto It = llvm::find_if(CombinedAllowed, [&](SemanticStageInfo Info) {
+    auto It = llvm::find_if(CombinedAllowed, [&](SemanticStageInfo &Info) {
       return Info.Stage == Case.Stage;
     });
     if (It == CombinedAllowed.end()) {
@@ -1148,7 +1148,7 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
     SmallVector<std::string, 8> ValidCases;
     llvm::transform(
         CombinedAllowed, std::back_inserter(ValidCases),
-        [](SemanticStageInfo Case) {
+        [](SemanticStageInfo &Case) {
           SmallVector<std::string, 2> ValidType;
           if (Case.AllowedIOTypesMask & IOType::In)
             ValidType.push_back("input");
@@ -1175,7 +1175,7 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
   SmallVector<StringRef, 8> StageStrings;
   llvm::transform(
       CombinedAllowed, std::back_inserter(StageStrings),
-      [](SemanticStageInfo Case) {
+      [](SemanticStageInfo &Case) {
         return StringRef(
             HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage));
       });
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 3f2d2946d44eb..1756f9cc978f4 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -33,8 +33,9 @@ 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).
+// Bitmask denoting whether a semantic is an input, output, inout or a value
+// that is constant across a patch (hull/domain shaders) or primitive
+// (mesh shaders).
 enum IOType {
   In = 0b001,
   Out = 0b010,
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 7a0e3a21564d4..753cd28563d6e 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -131,7 +131,7 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
   }
   default:
     llvm_unreachable(
-        "available stages for given semantic kind are not handled");
+        "available stages for given semantic kind are not handled yet");
   }
 }
 

>From 16f98ae114b29aae4e23126abd9b4c21a0cf6d8d Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 16:33:26 +0000
Subject: [PATCH 21/36] review: use an assert

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 66178493a9ccc..578c5fc53bc89 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1551,8 +1551,8 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
   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");
+  assert(Interpretation != llvm::hlsl::SemanticInterpretation::Invalid &&
+         "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,

>From 477aed64b1e173faa748753d646d7acd00b7f23a Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 16:36:09 +0000
Subject: [PATCH 22/36] review: improve diag function names

---
 clang/include/clang/Sema/SemaHLSL.h | 7 ++++---
 clang/lib/Sema/SemaHLSL.cpp         | 8 ++++----
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 3ecb6e6c61fe7..fa05921260646 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -231,9 +231,10 @@ class SemaHLSL : public SemaBase {
   QualType ActOnTemplateShorthand(TemplateDecl *Template,
                                   SourceLocation NameLoc);
 
-  // Diagnose whether the input ID is uint/unit2/uint3 type.
-  bool diagnoseInputIDType(QualType T, const ParsedAttr &AL);
-  bool diagnosePositionType(QualType T, const ParsedAttr &AL);
+  // Diagnose whether the index type is uint/unit2/uint3 type.
+  bool diagnoseIndexType(QualType T, const ParsedAttr &AL);
+  // Diagnose whether the type is float/float2/float3/float4 type.
+  bool diagnoseFloatType(QualType T, const ParsedAttr &AL);
 
   bool CanPerformScalarCast(QualType SrcTy, QualType DestTy);
   bool CanPerformElementwiseCast(Expr *Src, QualType DestType);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9341c0a3c9fdd..481497b4dbf86 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1904,7 +1904,7 @@ void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
                  HLSLVkLocationAttr(getASTContext(), AL, Location));
 }
 
-bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
+bool SemaHLSL::diagnoseIndexType(QualType T, const ParsedAttr &AL) {
   const auto *VT = T->getAs<VectorType>();
 
   if (!T->hasUnsignedIntegerRepresentation() ||
@@ -1917,7 +1917,7 @@ bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
   return true;
 }
 
-bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) {
+bool SemaHLSL::diagnoseFloatType(QualType T, const ParsedAttr &AL) {
   const auto *VT = T->getAs<VectorType>();
   if (!T->hasFloatingRepresentation() || (VT && VT->getNumElements() > 4)) {
     Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
@@ -1948,7 +1948,7 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
   case SemanticKind::DispatchThreadID:
   case SemanticKind::GroupThreadID:
   case SemanticKind::GroupID:
-    diagnoseInputIDType(ValueType, AL);
+    diagnoseIndexType(ValueType, AL);
     [[fallthrough]];
   case SemanticKind::GroupIndex:
     if (IsOutput)
@@ -1958,7 +1958,7 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
     break;
   case SemanticKind::Position:
   case SemanticKind::Target:
-    diagnosePositionType(ValueType, AL);
+    diagnoseFloatType(ValueType, AL);
     break;
   case SemanticKind::VertexID: {
     uint64_t SizeInBits = SemaRef.Context.getTypeSize(ValueType);

>From 2d2306a38c7a2415ec94bc41328bacfd764c6285 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 16:56:16 +0000
Subject: [PATCH 23/36] self-review: remove dead code

---
 clang/lib/Sema/SemaHLSL.cpp | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 481497b4dbf86..e386d80a7e35a 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1171,18 +1171,6 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
         << join(ValidCases, ", ");
     return;
   }
-
-  SmallVector<StringRef, 8> StageStrings;
-  llvm::transform(
-      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)
-      << (CombinedAllowed.size() != 1) << join(StageStrings, ", ");
 }
 
 template <CastKind Kind>

>From dfa25e6b6e1f60452a18b954d159d052144ff474 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 18:02:08 +0000
Subject: [PATCH 24/36] review: clean-up diagnose func

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +-
 clang/include/clang/Sema/SemaHLSL.h           |  9 +--
 clang/lib/Sema/SemaHLSL.cpp                   | 63 ++++++++-----------
 .../Semantics/arbitrary.ps.output.hlsl        |  2 +-
 clang/test/SemaHLSL/Semantics/groupindex.hlsl | 12 ++--
 .../Semantics/invalid_entry_parameter.hlsl    |  8 +--
 .../test/SemaHLSL/Semantics/position.ps.hlsl  |  2 +-
 .../SemaHLSL/Semantics/target.ps.input.hlsl   |  2 +-
 .../SemaHLSL/Semantics/target.vs.input.hlsl   |  2 +-
 .../SemaHLSL/Semantics/target.vs.output.hlsl  |  2 +-
 .../test/SemaHLSL/Semantics/vertexid.ps.hlsl  |  2 +-
 11 files changed, 48 insertions(+), 60 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9bd0a526654c7..b985a23035d79 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13682,8 +13682,8 @@ def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
-    : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of "
-            "the following: %3">;
+    : Error<"semantic %0 is not supported %select{in %1 shaders"
+            "|as a %1 shader %3, it is available as an %4}2">;
 def err_hlsl_semantic_partial_explicit_indexing
     : Error<"partial explicit stage input location assignment via "
             "vk::location(X) unsupported">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index fa05921260646..6c0e5b52f7cb3 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -334,10 +334,11 @@ class SemaHLSL : public SemaBase {
       const Attr *A, llvm::Triple::EnvironmentType Stage,
       std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
 
-  void diagnoseSemanticStageMismatch(
-      const Attr *A, llvm::Triple::EnvironmentType Stage,
-      llvm::hlsl::IOType CurrentIOType,
-      llvm::ArrayRef<llvm::hlsl::SemanticStageInfo> AllowedStages);
+  void
+  diagnoseSemanticStageMismatch(const Attr *A,
+                                llvm::Triple::EnvironmentType Stage,
+                                llvm::hlsl::IOType CurrentIOType,
+                                llvm::dxbc::PSV::SemanticKind SemanticKind);
 
   void handleGlobalStructOrArrayOfWithResources(VarDecl *VD);
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e386d80a7e35a..fd200ade60379 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1087,8 +1087,7 @@ void SemaHLSL::checkSemanticAnnotation(
   llvm::hlsl::SemanticInterpretation Interpretation =
       llvm::hlsl::getInterpretationKind(Kind, ST, SC.CurrentIOType);
   if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
-    diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
-                                  llvm::hlsl::getAvailableStages(Kind));
+    diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, Kind);
 
   switch (Kind) {
   case SemanticKind::DispatchThreadID:
@@ -1124,51 +1123,39 @@ void SemaHLSL::diagnoseAttrStageMismatch(
 
 void SemaHLSL::diagnoseSemanticStageMismatch(
     const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
-    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);
-  }
+    SemanticKind Kind) {
 
-  for (auto &Case : CombinedAllowed) {
-    if (Case.Stage != Stage)
-      continue;
+  ArrayRef<SemanticStageInfo> Allowed = llvm::hlsl::getAvailableStages(Kind);
+  auto It = llvm::find_if(Allowed, [&Stage](const SemanticStageInfo &Info) {
+    return Info.Stage == Stage;
+  });
 
-    if (CurrentIOType & Case.AllowedIOTypesMask)
-      return;
+  // The semantic is not available in this shader stage at all.
+  if (It == Allowed.end()) {
+    Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
+        << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
+        << /*AvailableInStage=*/false;
+    return;
+  }
 
-    SmallVector<std::string, 8> ValidCases;
-    llvm::transform(
-        CombinedAllowed, std::back_inserter(ValidCases),
-        [](SemanticStageInfo &Case) {
-          SmallVector<std::string, 2> ValidType;
-          if (Case.AllowedIOTypesMask & IOType::In)
-            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, "/");
-        });
+  auto AllowedIOTypes = It->AllowedIOTypesMask;
+  if (!(AllowedIOTypes & CurrentIOType)) {
     StringRef CurrentIOTypeName = "patch constant or primitive";
     if (CurrentIOType & IOType::In)
       CurrentIOTypeName = "input";
     else if (CurrentIOType & IOType::Out)
       CurrentIOTypeName = "output";
+    SmallVector<std::string, 3> ValidType;
+    if (AllowedIOTypes & IOType::In)
+      ValidType.push_back("input");
+    if (AllowedIOTypes & IOType::Out)
+      ValidType.push_back("output");
+    if (AllowedIOTypes & IOType::PatchConstantOrPrimitive)
+      ValidType.push_back("patch constant or primitive");
     Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
-        << A->getAttrName() << CurrentIOTypeName
-        << llvm::Triple::getEnvironmentTypeName(Case.Stage)
-        << join(ValidCases, ", ");
+        << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
+        << /*AvailableInStage=*/true << CurrentIOTypeName
+        << join(ValidType, ", ");
     return;
   }
 }
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
index 308bb48b096ba..9306af56503ec 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -2,6 +2,6 @@
 // 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}}
+// expected-error at -1 {{semantic 'B' is not supported as a pixel shader output, it is available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/groupindex.hlsl b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
index a33e060c82906..f6ce2105f3139 100644
--- a/clang/test/SemaHLSL/Semantics/groupindex.hlsl
+++ b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
@@ -4,26 +4,26 @@
 [shader("compute")][numthreads(32,1,1)]
 void compute(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'pixel' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in pixel shaders}}
 [shader("pixel")]
 void pixel(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in vertex shaders}}
 [shader("vertex")]
 void vertex(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'geometry' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in geometry shaders}}
 [shader("geometry")]
 void geometry(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'domain' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in domain shaders}}
 [shader("domain")]
 void domain(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'amplification' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in amplification shaders}}
 [shader("amplification")][numthreads(32,1,1)]
 void amplification(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'mesh' shaders}}
+// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in mesh shaders}}
 [shader("mesh")][numthreads(32,1,1)]
 void mesh(int GI : SV_GroupIndex) {}
diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
index 070075d419df1..200331869d24c 100644
--- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
+++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
@@ -74,8 +74,8 @@ struct ST2_GThreadID {
 
 
 [shader("vertex")]
-// expected-error at +4 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +3 {{attribute 'SV_DispatchThreadID' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +2 {{attribute 'SV_GroupID' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +1 {{attribute 'SV_GroupThreadID' is unsupported in 'vertex' shaders, requires compute}}
+// expected-error at +4 {{semantic 'SV_GroupIndex' is not supported in vertex shaders}}
+// expected-error at +3 {{semantic 'SV_DispatchThreadID' is not supported in vertex shaders}}
+// expected-error at +2 {{semantic 'SV_GroupID' is not supported in vertex shaders}}
+// expected-error at +1 {{semantic 'SV_GroupThreadID' is not supported in vertex shaders}}
 void vs_main(int GI : SV_GroupIndex, uint ID : SV_DispatchThreadID, uint GID : SV_GroupID, uint GThreadID : SV_GroupThreadID) {}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
index d0fe19d1a5407..ac053a91d0ec6 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : A) : SV_Position {
-// expected-error at -1 {{semantic 'SV_Position' is unsupported in pixel shaders as output, requires one of the following: vertex input/output, pixel input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output, it is available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index 87972640e294a..ace2639d37d6b 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 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}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input, it is available as an output}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl b/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl
index add24732fc05a..cc4e983dcc09d 100644
--- a/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl
@@ -2,7 +2,7 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-vertex -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Target) : A {
-// expected-error at -1 {{attribute 'SV_Target' is unsupported in 'vertex' shaders, requires pixel}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported in vertex shaders}}
   return a;
 }
 
diff --git a/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl b/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl
index 0481bcdad0177..89f1ccf1f2eb7 100644
--- a/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-vertex -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Position) : SV_Target {
-// expected-error at -1 {{attribute 'SV_Target' is unsupported in 'vertex' shaders, requires pixel}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported in vertex shaders}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/vertexid.ps.hlsl b/clang/test/SemaHLSL/Semantics/vertexid.ps.hlsl
index d205e099149cb..1d26edfc5b8db 100644
--- a/clang/test/SemaHLSL/Semantics/vertexid.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/vertexid.ps.hlsl
@@ -2,7 +2,7 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(uint id : SV_VertexID) : SV_Target {
-// expected-error at -1 {{attribute 'SV_VertexID' is unsupported in 'pixel' shaders, requires vertex}}
+// expected-error at -1 {{semantic 'SV_VertexID' is not supported in pixel shaders}}
   return float4(1, 1, 1, 1);
 }
 

>From e5b23934352ce756264acecb3400286301619440 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 18:28:43 +0000
Subject: [PATCH 25/36] review: correct iotype as a bitmask

---
 clang/lib/Sema/SemaHLSL.cpp                        | 14 +++++++-------
 .../llvm/Frontend/HLSL/SemanticSignatures.h        |  5 ++++-
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp      |  4 ++--
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index fd200ade60379..65a2e60d118cf 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -919,7 +919,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
   unsigned Location = ActiveSemantic.Index.value_or(0);
 
   if (!isVkPipelineBuiltin(getASTContext(), FD, A,
-                           SC.CurrentIOType & IOType::In)) {
+                           any(SC.CurrentIOType & IOType::In))) {
     bool HasVkLocation = false;
     if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
       HasVkLocation = true;
@@ -1138,19 +1138,19 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
     return;
   }
 
-  auto AllowedIOTypes = It->AllowedIOTypesMask;
+  IOType AllowedIOTypes = It->AllowedIOTypesMask;
   if (!(AllowedIOTypes & CurrentIOType)) {
     StringRef CurrentIOTypeName = "patch constant or primitive";
-    if (CurrentIOType & IOType::In)
+    if (any(CurrentIOType & IOType::In))
       CurrentIOTypeName = "input";
-    else if (CurrentIOType & IOType::Out)
+    else if (any(CurrentIOType & IOType::Out))
       CurrentIOTypeName = "output";
     SmallVector<std::string, 3> ValidType;
-    if (AllowedIOTypes & IOType::In)
+    if (any(AllowedIOTypes & IOType::In))
       ValidType.push_back("input");
-    if (AllowedIOTypes & IOType::Out)
+    if (any(AllowedIOTypes & IOType::Out))
       ValidType.push_back("output");
-    if (AllowedIOTypes & IOType::PatchConstantOrPrimitive)
+    if (any(AllowedIOTypes & IOType::PatchConstantOrPrimitive))
       ValidType.push_back("patch constant or primitive");
     Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
         << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 1756f9cc978f4..975fc2032d46e 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -15,6 +15,7 @@
 #define LLVM_FRONTEND_HLSL_SEMANTICSIGNATURES_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/BinaryFormat/DXContainer.h"
@@ -31,12 +32,14 @@ class MDNode;
 
 namespace hlsl {
 
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
 // Definitions of the in-memory data layout structures
 
 // Bitmask denoting whether a semantic is an input, output, inout or a value
 // that is constant across a patch (hull/domain shaders) or primitive
 // (mesh shaders).
-enum IOType {
+enum class IOType {
   In = 0b001,
   Out = 0b010,
   InOut = 0b011,
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 753cd28563d6e..9d4bb2b3ba7b7 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -60,7 +60,7 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
   switch (SemanticKind) {
   case dxbc::PSV::SemanticKind::Arbitrary: {
     static constexpr IOType OutOrPatchConstant =
-        static_cast<IOType>(IOType::Out | IOType::PatchConstantOrPrimitive);
+        IOType::Out | IOType::PatchConstantOrPrimitive;
     static constexpr SemanticStageInfo Stages[] = {
         {Triple::Vertex, IOType::InOut, SemanticInterpretation::Arbitrary},
         {Triple::Geometry, IOType::InOut, SemanticInterpretation::Arbitrary},
@@ -139,7 +139,7 @@ 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))
+    if (Info.Stage == ShaderStage && any(Info.AllowedIOTypesMask & IOTy))
       return Info.Interpretation;
   return SemanticInterpretation::Invalid;
 }

>From 2fe71fa30c726bf02a595a473f1b44e78f5ecf22 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 18:39:22 +0000
Subject: [PATCH 26/36] review: correct stages to be completed

---
 clang/test/SemaHLSL/Semantics/groupindex.hlsl |  2 --
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 17 +++++++++++++++--
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/clang/test/SemaHLSL/Semantics/groupindex.hlsl b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
index f6ce2105f3139..d07db9f4f57b4 100644
--- a/clang/test/SemaHLSL/Semantics/groupindex.hlsl
+++ b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
@@ -20,10 +20,8 @@ void geometry(int GI : SV_GroupIndex) {}
 [shader("domain")]
 void domain(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in amplification shaders}}
 [shader("amplification")][numthreads(32,1,1)]
 void amplification(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{semantic 'SV_GroupIndex' is not supported in mesh shaders}}
 [shader("mesh")][numthreads(32,1,1)]
 void mesh(int GI : SV_GroupIndex) {}
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 9d4bb2b3ba7b7..c1f66bd7daede 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -76,7 +76,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
   case dxbc::PSV::SemanticKind::GroupIndex:
   case dxbc::PSV::SemanticKind::GroupThreadID: {
     static constexpr SemanticStageInfo Stages[] = {
-        {Triple::Compute, IOType::In, SemanticInterpretation::NotAllocated}};
+        {Triple::Compute, IOType::In, SemanticInterpretation::NotAllocated},
+        {Triple::Mesh, IOType::In, SemanticInterpretation::NotAllocated},
+        {Triple::Amplification, IOType::In,
+         SemanticInterpretation::NotAllocated},
+    };
     return Stages;
   }
   case dxbc::PSV::SemanticKind::Target: {
@@ -99,7 +103,16 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
     static constexpr SemanticStageInfo Stages[] = {
         {Triple::Vertex, IOType::In, SemanticInterpretation::Arbitrary},
         {Triple::Vertex, IOType::Out, SemanticInterpretation::SV},
-        {Triple::Pixel, IOType::In, SemanticInterpretation::SV}};
+        {Triple::Hull, IOType::InOut, SemanticInterpretation::SV},
+        {Triple::Hull, IOType::PatchConstantOrPrimitive,
+         SemanticInterpretation::Arbitrary},
+        {Triple::Domain, IOType::InOut, SemanticInterpretation::SV},
+        {Triple::Domain, IOType::PatchConstantOrPrimitive,
+         SemanticInterpretation::Arbitrary},
+        {Triple::Geometry, IOType::InOut, SemanticInterpretation::SV},
+        {Triple::Pixel, IOType::In, SemanticInterpretation::SV},
+        {Triple::Mesh, IOType::Out, SemanticInterpretation::SV},
+    };
     return Stages;
   }
   case dxbc::PSV::SemanticKind::ClipDistance:

>From dea4018fcd8598734f408532ed48b759eb35454c Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 18:45:07 +0000
Subject: [PATCH 27/36] review: assert a single iotype in getInterpretationKind

---
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index c1f66bd7daede..0c521342e487f 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -14,10 +14,12 @@
 #include "llvm/Frontend/HLSL/SemanticSignatures.h"
 #include "llvm/ADT/Enum.h"
 #include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/bit.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Type.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <cassert>
 
 using namespace llvm;
 using namespace llvm::hlsl;
@@ -151,6 +153,8 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
 SemanticInterpretation
 hlsl::getInterpretationKind(dxbc::PSV::SemanticKind SemanticKind,
                             Triple::EnvironmentType ShaderStage, IOType IOTy) {
+  assert(llvm::has_single_bit(static_cast<unsigned>(IOTy)) &&
+         "a single IOType is expected, not a mask of IOTypes");
   for (const SemanticStageInfo &Info : getAvailableStages(SemanticKind))
     if (Info.Stage == ShaderStage && any(Info.AllowedIOTypesMask & IOTy))
       return Info.Interpretation;

>From 69e4fff2dcc43272bf4218d069e9773d425707e5 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 19:01:32 +0000
Subject: [PATCH 28/36] review: add invariant unit tests of stage tables

---
 llvm/unittests/Frontend/CMakeLists.txt        |   1 +
 .../Frontend/HLSLSemanticStagesTest.cpp       | 102 ++++++++++++++++++
 2 files changed, 103 insertions(+)
 create mode 100644 llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp

diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index 8976dd1b2f737..b8a26561069d5 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -16,6 +16,7 @@ add_llvm_unittest(LLVMFrontendTests
   EnumSetTest.cpp
   HLSLBindingTest.cpp
   HLSLRootSignatureDumpTest.cpp
+  HLSLSemanticStagesTest.cpp
   HLSLSemanticSignatureMetadataTest.cpp
   OpenACCTest.cpp
   OpenMPContextTest.cpp
diff --git a/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp b/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp
new file mode 100644
index 0000000000000..d6aef45c4f5bf
--- /dev/null
+++ b/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp
@@ -0,0 +1,102 @@
+//===- HLSLSemanticStagesTest.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/HLSL/SemanticSignatures.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <ostream>
+
+using namespace llvm;
+using namespace llvm::hlsl;
+
+namespace llvm::hlsl {
+
+static StringRef getInterpretationName(SemanticInterpretation Interpretation) {
+  switch (Interpretation) {
+  case SemanticInterpretation::Invalid:
+    return "Invalid";
+  case SemanticInterpretation::NotAllocated:
+    return "NotAllocated";
+  case SemanticInterpretation::Arbitrary:
+    return "Arbitrary";
+  case SemanticInterpretation::SV:
+    return "SV";
+  case SemanticInterpretation::SGV:
+    return "SGV";
+  case SemanticInterpretation::ClipCull:
+    return "ClipCull";
+  case SemanticInterpretation::TessFactor:
+    return "TessFactor";
+  case SemanticInterpretation::Target:
+    return "Target";
+  }
+  llvm_unreachable("unhandled interpretation");
+}
+
+// Print the enum by name to keep the test failures readable.
+static void PrintTo(SemanticInterpretation Interpretation, std::ostream *OS) {
+  *OS << getInterpretationName(Interpretation).str();
+}
+
+} // namespace llvm::hlsl
+
+namespace {
+
+using SemanticKind = dxbc::PSV::SemanticKind;
+
+// Every shader stage a semantic could possibly be used in, plus a stage that
+// never holds a signature (Library) to make sure it is always rejected.
+constexpr Triple::EnvironmentType AllStages[] = {
+    Triple::Vertex,        Triple::Hull,  Triple::Domain,
+    Triple::Geometry,      Triple::Pixel, Triple::Compute,
+    Triple::Amplification, Triple::Mesh,  Triple::Library};
+
+// The semantic kinds getAvailableStages knows about.
+constexpr SemanticKind SupportedKinds[] = {
+    SemanticKind::Arbitrary,        SemanticKind::Position,
+    SemanticKind::VertexID,         SemanticKind::Target,
+    SemanticKind::IsFrontFace,      SemanticKind::ClipDistance,
+    SemanticKind::CullDistance,     SemanticKind::TessFactor,
+    SemanticKind::InsideTessFactor, SemanticKind::DispatchThreadID,
+    SemanticKind::GroupID,          SemanticKind::GroupIndex,
+    SemanticKind::GroupThreadID};
+
+// Ensure all stages return a valid SemanticInterpretation
+TEST(HLSLSemanticStagesTest, StagesAreValid) {
+  for (SemanticKind Kind : SupportedKinds) {
+    ArrayRef<SemanticStageInfo> Stages = getAvailableStages(Kind);
+    EXPECT_FALSE(Stages.empty());
+    for (const SemanticStageInfo &Info : Stages) {
+      EXPECT_TRUE(any(Info.AllowedIOTypesMask))
+          << "stage " << Triple::getEnvironmentTypeName(Info.Stage).str()
+          << " allows no IOType";
+      EXPECT_NE(Info.Interpretation, SemanticInterpretation::Invalid);
+      EXPECT_THAT(AllStages, testing::Contains(Info.Stage));
+    }
+  }
+}
+
+// Ensure a stage is not listed twice with overlapping IOTypes
+TEST(HLSLSemanticStagesTest, StageIOTypesAreDisjoint) {
+  for (SemanticKind Kind : SupportedKinds) {
+    ArrayRef<SemanticStageInfo> Stages = getAvailableStages(Kind);
+    for (Triple::EnvironmentType Stage : AllStages) {
+      IOType Seen = static_cast<IOType>(0);
+      for (const SemanticStageInfo &Info : Stages) {
+        if (Info.Stage != Stage)
+          continue;
+        EXPECT_FALSE(any(Seen & Info.AllowedIOTypesMask))
+            << "stage " << Triple::getEnvironmentTypeName(Stage).str()
+            << " is listed twice for the same IOType";
+        Seen |= Info.AllowedIOTypesMask;
+      }
+    }
+  }
+}
+
+} // namespace

>From 78a809ed110ea95fb3b80d4947445cc169ac33fa Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 19:08:26 +0000
Subject: [PATCH 29/36] review: correct double diag

---
 clang/lib/Sema/SemaHLSL.cpp                   |  5 +++--
 .../semantics/DispatchThreadID-noindex.hlsl   |  4 ++--
 .../semantics/SV_GroupID-noindex.hlsl         |  4 ++--
 .../semantics/SV_GroupThreadID-noindex.hlsl   |  4 ++--
 .../SemaHLSL/Semantics/semantic-indexing.hlsl | 19 +++++++++++++++++++
 5 files changed, 28 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 65a2e60d118cf..c28d072397b27 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1928,8 +1928,9 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
   case SemanticKind::GroupIndex:
     if (IsOutput)
       Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
-    if (Index.has_value())
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << AL;
+    // Indexing is diagnosed in checkSemanticAnnotation, where the semantic
+    // index of the entry point signature is known. It can be explicit, like
+    // here, or derived when a semantic is spread over an aggregate.
     break;
   case SemanticKind::Position:
   case SemanticKind::Target:
diff --git a/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl
index b41bb0b0e8995..cf0c75479ddd9 100644
--- a/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/DispatchThreadID-noindex.hlsl
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
-// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
 
 [shader("compute")]
 [numthreads(8,8,1)]
diff --git a/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl
index 795e880fba0fd..31f5bca195d57 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_GroupID-noindex.hlsl
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
-// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
 
 [shader("compute")]
 [numthreads(8,8,1)]
diff --git a/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl b/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl
index 1fd5ae4ff488e..5f34118cdc4b0 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_GroupThreadID-noindex.hlsl
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
-// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note,error
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
 
 [shader("compute")]
 [numthreads(8,8,1)]
diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
new file mode 100644
index 0000000000000..b78d165c164b1
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+struct Pair {
+  uint A;
+  uint B;
+};
+
+// The semantic index can be written explicitly ...
+[shader("compute")][numthreads(1,1,1)]
+void explicit_index(uint GI : SV_GroupIndex1) {}
+// expected-error at -1 {{semantic 'SV_GroupIndex' does not allow indexing}}
+
+// ... or be derived when a semantic is spread over an aggregate.
+[shader("compute")][numthreads(1,1,1)]
+void derived_index(Pair GI : SV_GroupIndex) {}
+// expected-error at -1 {{semantic 'SV_GroupIndex' does not allow indexing}}
+
+[shader("compute")][numthreads(1,1,1)]
+void no_index(uint GI : SV_GroupIndex) {}

>From cca419a759598999ce65c0bbdd8dddd6805fa6a2 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 19:20:03 +0000
Subject: [PATCH 30/36] self-review: remove redundant error

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 --
 clang/lib/Sema/SemaHLSL.cpp                   | 28 ++++++++-----------
 .../SemaHLSL/Semantics/output-parameters.hlsl | 26 +++++++++++++++++
 3 files changed, 38 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/output-parameters.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b985a23035d79..719c6c62801a5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13674,8 +13674,6 @@ def err_hlsl_missing_semantic_annotation : Error<
   "function or patch constant function">;
 def note_hlsl_semantic_used_here : Note<"%0 used here">;
 def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">;
-def err_hlsl_semantic_output_not_supported
-    : Error<"semantic %0 does not support output">;
 def err_hlsl_semantic_indexing_not_supported
     : Error<"semantic %0 does not allow indexing">;
 def err_hlsl_init_priority_unsupported : Error<
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index c28d072397b27..c98ef25fba2a3 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1051,6 +1051,8 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
 
   SemaHLSL::SemanticContext InputSC = {};
   InputSC.CurrentIOType = IOType::In;
+  SemaHLSL::SemanticContext OutputSC = {};
+  OutputSC.CurrentIOType = IOType::Out;
 
   for (ParmVarDecl *Param : FD->parameters()) {
     SemanticInfo ActiveSemantic;
@@ -1058,16 +1060,18 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
     if (ActiveSemantic.Semantic)
       ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
 
-    // FIXME: Verify output semantics in parameters.
-    if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, InputSC)) {
+    // FIXME: An `inout` parameter is part of both signatures, but it is only
+    // verified against the output one here.
+    const auto *MA = Param->getAttr<HLSLParamModifierAttr>();
+    SemanticContext &SC = MA && MA->isAnyOut() ? OutputSC : InputSC;
+
+    if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, SC)) {
       Diag(Param->getLocation(), diag::note_previous_decl) << Param;
       FD->setInvalidDecl();
     }
   }
 
   SemanticInfo ActiveSemantic;
-  SemaHLSL::SemanticContext OutputSC = {};
-  OutputSC.CurrentIOType = IOType::Out;
   ActiveSemantic.Semantic = FD->getAttr<HLSLParsedSemanticAttr>();
   if (ActiveSemantic.Semantic)
     ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
@@ -1911,26 +1915,18 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
   if (auto *FD = dyn_cast<FunctionDecl>(D))
     ValueType = FD->getReturnType();
 
-  bool IsOutput = false;
-  if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>()) {
-    if (MA->isOut()) {
-      IsOutput = true;
+  // `out` and `inout` parameters are passed by reference.
+  if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>())
+    if (MA->isAnyOut())
       ValueType = cast<ReferenceType>(ValueType)->getPointeeType();
-    }
-  }
 
   switch (Kind) {
   case SemanticKind::DispatchThreadID:
   case SemanticKind::GroupThreadID:
   case SemanticKind::GroupID:
     diagnoseIndexType(ValueType, AL);
-    [[fallthrough]];
+    break;
   case SemanticKind::GroupIndex:
-    if (IsOutput)
-      Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;
-    // Indexing is diagnosed in checkSemanticAnnotation, where the semantic
-    // index of the entry point signature is known. It can be explicit, like
-    // here, or derived when a semantic is spread over an aggregate.
     break;
   case SemanticKind::Position:
   case SemanticKind::Target:
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
new file mode 100644
index 0000000000000..fe6eb2006aeee
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+// Parameters passed by reference are written by the entry point, so they are
+// verified against the output signature of the shader stage.
+
+[shader("vertex")]
+void vs_out(out float4 Pos : SV_Position) { Pos = 0; }
+
+[shader("pixel")]
+void ps_out(out float4 Color : SV_Target) { Color = 0; }
+
+[shader("pixel")]
+void ps_position_out(out float4 Pos : SV_Position) { Pos = 0; }
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output, it is available as an input}}
+
+[shader("compute")][numthreads(1,1,1)]
+void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; }
+// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output, it is available as an input}}
+
+// Output parameters share the output signature with the return value.
+[shader("pixel")]
+float4 ps_overlap(out float4 Color : SV_Target) : SV_Target {
+// expected-error at -1 {{semantic index overlap SV_Target0}}
+  Color = 0;
+  return 0;
+}

>From e9c77001d8a82c772e816bc076a16bfcd7253c25 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 20:12:16 +0000
Subject: [PATCH 31/36] review: return empty instead of crash

---
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 0c521342e487f..7c4480075d6af 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -145,8 +145,7 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
     return Stages;
   }
   default:
-    llvm_unreachable(
-        "available stages for given semantic kind are not handled yet");
+    return {};
   }
 }
 

>From 69b3b236a8a4d6d8735faf136a1918c2620dac7a Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 8 Sep 2026 21:19:23 +0000
Subject: [PATCH 32/36] review: fix grammar typo

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td       | 2 +-
 clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl | 2 +-
 clang/test/SemaHLSL/Semantics/output-parameters.hlsl   | 4 ++--
 clang/test/SemaHLSL/Semantics/position.ps.hlsl         | 2 +-
 clang/test/SemaHLSL/Semantics/target.ps.input.hlsl     | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 719c6c62801a5..7d499ce4e3b1a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13681,7 +13681,7 @@ def err_hlsl_init_priority_unsupported : Error<
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported %select{in %1 shaders"
-            "|as a %1 shader %3, it is available as an %4}2">;
+            "|as a %1 shader %3; it is available as an %4}2">;
 def err_hlsl_semantic_partial_explicit_indexing
     : Error<"partial explicit stage input location assignment via "
             "vk::location(X) unsupported">;
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
index 9306af56503ec..b29baa9e39529 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -2,6 +2,6 @@
 // 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 not supported as a pixel shader output, it is available as an input}}
+// expected-error at -1 {{semantic 'B' is not supported as a pixel shader output; it is available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
index fe6eb2006aeee..85397e094174c 100644
--- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -11,11 +11,11 @@ void ps_out(out float4 Color : SV_Target) { Color = 0; }
 
 [shader("pixel")]
 void ps_position_out(out float4 Pos : SV_Position) { Pos = 0; }
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output, it is available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is available as an input}}
 
 [shader("compute")][numthreads(1,1,1)]
 void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; }
-// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output, it is available as an input}}
+// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output; it is available as an input}}
 
 // Output parameters share the output signature with the return value.
 [shader("pixel")]
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
index ac053a91d0ec6..ddc8841393e5c 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : A) : SV_Position {
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output, it is available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index ace2639d37d6b..a140d94cfb7b1 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Target) : SV_Target {
-// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input, it is available as an output}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input; it is available as an output}}
   return a;
 }

>From 10cc2f4a76dd3e46edf34a8569a79de139c4f692 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 17:38:33 +0000
Subject: [PATCH 33/36] review: update a(n) in error message

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td       | 2 +-
 clang/lib/Sema/SemaHLSL.cpp                            | 6 +++---
 clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl  | 7 +++++++
 clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl | 2 +-
 clang/test/SemaHLSL/Semantics/output-parameters.hlsl   | 4 ++--
 clang/test/SemaHLSL/Semantics/position.ps.hlsl         | 2 +-
 clang/test/SemaHLSL/Semantics/target.ps.input.hlsl     | 2 +-
 7 files changed, 16 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7d499ce4e3b1a..b6b91eb82de27 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13681,7 +13681,7 @@ def err_hlsl_init_priority_unsupported : Error<
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported %select{in %1 shaders"
-            "|as a %1 shader %3; it is available as an %4}2">;
+            "|as a %1 shader %3; it is only available as %4}2">;
 def err_hlsl_semantic_partial_explicit_indexing
     : Error<"partial explicit stage input location assignment via "
             "vk::location(X) unsupported">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index c98ef25fba2a3..a348da02d7a84 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1151,11 +1151,11 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
       CurrentIOTypeName = "output";
     SmallVector<std::string, 3> ValidType;
     if (any(AllowedIOTypes & IOType::In))
-      ValidType.push_back("input");
+      ValidType.push_back("an input");
     if (any(AllowedIOTypes & IOType::Out))
-      ValidType.push_back("output");
+      ValidType.push_back("an output");
     if (any(AllowedIOTypes & IOType::PatchConstantOrPrimitive))
-      ValidType.push_back("patch constant or primitive");
+      ValidType.push_back("a patch constant or a primitive");
     Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
         << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
         << /*AvailableInStage=*/true << CurrentIOTypeName
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
new file mode 100644
index 0000000000000..e569533d596c5
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-mesh -finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-mesh -finclude-default-header -x hlsl -verify -o - %s
+
+[numthreads(1,1,1)]
+void main(uint a : A) {
+// expected-error at -1 {{semantic 'A' is not supported as a mesh shader input; it is only available as an output, a patch constant or a primitive}}
+}
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
index b29baa9e39529..4e9875b8802d4 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -2,6 +2,6 @@
 // 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 not supported as a pixel shader output; it is available as an input}}
+// expected-error at -1 {{semantic 'B' is not supported as a pixel shader output; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
index 85397e094174c..0f135e34db24d 100644
--- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -11,11 +11,11 @@ void ps_out(out float4 Color : SV_Target) { Color = 0; }
 
 [shader("pixel")]
 void ps_position_out(out float4 Pos : SV_Position) { Pos = 0; }
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is only available as an input}}
 
 [shader("compute")][numthreads(1,1,1)]
 void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; }
-// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output; it is available as an input}}
+// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output; it is only available as an input}}
 
 // Output parameters share the output signature with the return value.
 [shader("pixel")]
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
index ddc8841393e5c..8e5b3d3decaab 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : A) : SV_Position {
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index a140d94cfb7b1..d92bc5bfdaab3 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Target) : SV_Target {
-// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input; it is available as an output}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input; it is only available as an output}}
   return a;
 }

>From 7bad6984e94af77a8c5096479c03ef159d61f4d2 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 18:04:54 +0000
Subject: [PATCH 34/36] review: add fixmes to missing semantic implementations

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp | 74 ++++++++++++++++++++++++++---
 clang/lib/CodeGen/CGHLSLRuntime.h   |  4 +-
 2 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 578c5fc53bc89..beeb919748994 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1437,15 +1437,34 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
 llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
     HLSLAppliedSemanticAttr *Semantic,
-    llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+    llvm::dxbc::PSV::SemanticKind SemanticKind,
+    llvm::Triple::EnvironmentType Stage, std::optional<unsigned> Index,
     SemanticSignatures &Signature) {
   switch (SemanticKind) {
   case llvm::dxbc::PSV::SemanticKind::GroupIndex: {
+    assert(llvm::is_contained({llvm::Triple::Compute, llvm::Triple::Mesh,
+                               llvm::Triple::Amplification},
+                              Stage) &&
+           "SV_GroupIndex is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    assert(Stage != llvm::Triple::Mesh &&
+           Stage != llvm::Triple::Amplification &&
+           "FIXME: SV_GroupIndex is not yet implemented for this shader "
+           "stage");
     llvm::Function *GroupIndex =
         CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic());
     return B.CreateCall(FunctionCallee(GroupIndex));
   }
   case llvm::dxbc::PSV::SemanticKind::DispatchThreadID: {
+    assert(llvm::is_contained({llvm::Triple::Compute, llvm::Triple::Mesh,
+                               llvm::Triple::Amplification},
+                              Stage) &&
+           "SV_DispatchThreadID is in an unavailable stage and should have "
+           "been diagnosed by Sema");
+    assert(Stage != llvm::Triple::Mesh &&
+           Stage != llvm::Triple::Amplification &&
+           "FIXME: SV_DispatchThreadID is not yet implemented for this "
+           "shader stage");
     llvm::Intrinsic::ID IntrinID = getThreadIdIntrinsic();
     llvm::Function *ThreadIDIntrinsic =
         llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1454,6 +1473,15 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     return buildVectorInput(B, ThreadIDIntrinsic, Type);
   }
   case llvm::dxbc::PSV::SemanticKind::GroupThreadID: {
+    assert(llvm::is_contained({llvm::Triple::Compute, llvm::Triple::Mesh,
+                               llvm::Triple::Amplification},
+                              Stage) &&
+           "SV_GroupThreadID is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    assert(Stage != llvm::Triple::Mesh &&
+           Stage != llvm::Triple::Amplification &&
+           "FIXME: SV_GroupThreadID is not yet implemented for this shader "
+           "stage");
     llvm::Intrinsic::ID IntrinID = getGroupThreadIdIntrinsic();
     llvm::Function *GroupThreadIDIntrinsic =
         llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1462,6 +1490,14 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     return buildVectorInput(B, GroupThreadIDIntrinsic, Type);
   }
   case llvm::dxbc::PSV::SemanticKind::GroupID: {
+    assert(llvm::is_contained({llvm::Triple::Compute, llvm::Triple::Mesh,
+                               llvm::Triple::Amplification},
+                              Stage) &&
+           "SV_GroupID is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    assert(Stage != llvm::Triple::Mesh &&
+           Stage != llvm::Triple::Amplification &&
+           "FIXME: SV_GroupID is not yet implemented for this shader stage");
     llvm::Intrinsic::ID IntrinID = getGroupIdIntrinsic();
     llvm::Function *GroupIDIntrinsic =
         llvm::Intrinsic::isOverloaded(IntrinID)
@@ -1470,6 +1506,15 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     return buildVectorInput(B, GroupIDIntrinsic, Type);
   }
   case llvm::dxbc::PSV::SemanticKind::Position:
+    assert(llvm::is_contained({llvm::Triple::Hull, llvm::Triple::Domain,
+                               llvm::Triple::Geometry, llvm::Triple::Pixel},
+                              Stage) &&
+           "SV_Position is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    assert(Stage != llvm::Triple::Hull && Stage != llvm::Triple::Domain &&
+           Stage != llvm::Triple::Geometry &&
+           "FIXME: loading SV_Position is not yet implemented for this "
+           "shader stage");
     if (CGM.getTarget().getTriple().isSPIRV())
       return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
                                     Semantic->getAttrName()->getName(),
@@ -1479,6 +1524,9 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
                                       Signature);
     break;
   case llvm::dxbc::PSV::SemanticKind::VertexID:
+    assert(Stage == llvm::Triple::Vertex &&
+           "SV_VertexID is in an unavailable stage and should have been "
+           "diagnosed by Sema");
     if (CGM.getTarget().getTriple().isSPIRV())
       return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
                                     Semantic->getAttrName()->getName(),
@@ -1512,10 +1560,21 @@ static void createSPIRVBuiltinStore(IRBuilder<> &B, llvm::Module &M,
 void CGHLSLRuntime::emitSystemSemanticStore(
     IRBuilder<> &B, llvm::Value *Source, const clang::DeclaratorDecl *Decl,
     HLSLAppliedSemanticAttr *Semantic,
-    llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+    llvm::dxbc::PSV::SemanticKind SemanticKind,
+    llvm::Triple::EnvironmentType Stage, std::optional<unsigned> Index,
     SemanticSignatures &Signature) {
   switch (SemanticKind) {
   case llvm::dxbc::PSV::SemanticKind::Position:
+    assert(llvm::is_contained({llvm::Triple::Vertex, llvm::Triple::Hull,
+                               llvm::Triple::Domain, llvm::Triple::Geometry,
+                               llvm::Triple::Mesh},
+                              Stage) &&
+           "SV_Position is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    assert(Stage != llvm::Triple::Hull && Stage != llvm::Triple::Domain &&
+           Stage != llvm::Triple::Geometry && Stage != llvm::Triple::Mesh &&
+           "FIXME: storing SV_Position is not yet implemented for this "
+           "shader stage");
     if (CGM.getTarget().getTriple().isDXIL()) {
       emitDXILUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
       return;
@@ -1528,6 +1587,9 @@ void CGHLSLRuntime::emitSystemSemanticStore(
     }
     break;
   case llvm::dxbc::PSV::SemanticKind::Target:
+    assert(Stage == llvm::Triple::Pixel &&
+           "SV_Target is in an unavailable stage and should have been "
+           "diagnosed by Sema");
     emitUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
     return;
   default:
@@ -1555,8 +1617,8 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
          "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);
+  return emitSystemSemanticLoad(B, Type, Decl, Semantic, SemanticKind,
+                                ShaderAttr->getType(), Index, Signature);
 }
 
 void CGHLSLRuntime::handleScalarSemanticStore(IRBuilder<> &B,
@@ -1579,8 +1641,8 @@ void CGHLSLRuntime::handleScalarSemanticStore(IRBuilder<> &B,
 
   if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary)
     return emitUserSemanticStore(B, Source, Decl, Semantic, Index, Signature);
-  emitSystemSemanticStore(B, Source, Decl, Semantic, SemanticKind, Index,
-                          Signature);
+  emitSystemSemanticStore(B, Source, Decl, Semantic, SemanticKind,
+                          ShaderAttr->getType(), 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 7fb54c033d74f..f551bb006ea2c 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -225,13 +225,15 @@ class CGHLSLRuntime {
   llvm::Value *emitSystemSemanticLoad(
       llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
       HLSLAppliedSemanticAttr *Semantic,
-      llvm::dxbc::PSV::SemanticKind SemanticKind, std::optional<unsigned> Index,
+      llvm::dxbc::PSV::SemanticKind SemanticKind,
+      llvm::Triple::EnvironmentType Stage, 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,
+                               llvm::Triple::EnvironmentType Stage,
                                std::optional<unsigned> Index,
                                SemanticSignatures &Signature);
 

>From 22374cbc5cf0381e92cf9bd723a5ec92597fda49 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 18:18:39 +0000
Subject: [PATCH 35/36] review: correct more grammar

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td       | 2 +-
 clang/lib/Sema/SemaHLSL.cpp                            | 6 +++---
 clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl  | 2 +-
 clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl | 2 +-
 clang/test/SemaHLSL/Semantics/output-parameters.hlsl   | 4 ++--
 clang/test/SemaHLSL/Semantics/position.ps.hlsl         | 2 +-
 clang/test/SemaHLSL/Semantics/target.ps.input.hlsl     | 2 +-
 7 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b6b91eb82de27..712b3489207cd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13681,7 +13681,7 @@ def err_hlsl_init_priority_unsupported : Error<
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported %select{in %1 shaders"
-            "|as a %1 shader %3; it is only available as %4}2">;
+            "|as %1 shader %3; it is only available as %4}2">;
 def err_hlsl_semantic_partial_explicit_indexing
     : Error<"partial explicit stage input location assignment via "
             "vk::location(X) unsupported">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a348da02d7a84..9ddd58eb363c9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1144,11 +1144,11 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
 
   IOType AllowedIOTypes = It->AllowedIOTypesMask;
   if (!(AllowedIOTypes & CurrentIOType)) {
-    StringRef CurrentIOTypeName = "patch constant or primitive";
+    StringRef CurrentIOTypeName = "patch constants or primitives";
     if (any(CurrentIOType & IOType::In))
-      CurrentIOTypeName = "input";
+      CurrentIOTypeName = "inputs";
     else if (any(CurrentIOType & IOType::Out))
-      CurrentIOTypeName = "output";
+      CurrentIOTypeName = "outputs";
     SmallVector<std::string, 3> ValidType;
     if (any(AllowedIOTypes & IOType::In))
       ValidType.push_back("an input");
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
index e569533d596c5..0fc3673afe3cb 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
@@ -3,5 +3,5 @@
 
 [numthreads(1,1,1)]
 void main(uint a : A) {
-// expected-error at -1 {{semantic 'A' is not supported as a mesh shader input; it is only available as an output, a patch constant or a primitive}}
+// expected-error at -1 {{semantic 'A' is not supported as mesh shader inputs; it is only available as an output, a patch constant or a primitive}}
 }
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
index 4e9875b8802d4..ac086a29e0f4d 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -2,6 +2,6 @@
 // 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 not supported as a pixel shader output; it is only available as an input}}
+// expected-error at -1 {{semantic 'B' is not supported as pixel shader outputs; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
index 0f135e34db24d..ede086a68d1de 100644
--- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -11,11 +11,11 @@ void ps_out(out float4 Color : SV_Target) { Color = 0; }
 
 [shader("pixel")]
 void ps_position_out(out float4 Pos : SV_Position) { Pos = 0; }
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as pixel shader outputs; it is only available as an input}}
 
 [shader("compute")][numthreads(1,1,1)]
 void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; }
-// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as a compute shader output; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as compute shader outputs; it is only available as an input}}
 
 // Output parameters share the output signature with the return value.
 [shader("pixel")]
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
index 8e5b3d3decaab..c8d2a9db796c5 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : A) : SV_Position {
-// expected-error at -1 {{semantic 'SV_Position' is not supported as a pixel shader output; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported as pixel shader outputs; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index d92bc5bfdaab3..fb7761cc6faf8 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Target) : SV_Target {
-// expected-error at -1 {{semantic 'SV_Target' is not supported as a pixel shader input; it is only available as an output}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported as pixel shader inputs; it is only available as an output}}
   return a;
 }

>From 480514b58a5aeb71d3a37632e795594c5d2c7929 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 9 Sep 2026 21:49:05 +0000
Subject: [PATCH 36/36] self-review: one last wording touch up

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td       | 2 +-
 clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl  | 2 +-
 clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl | 2 +-
 clang/test/SemaHLSL/Semantics/output-parameters.hlsl   | 4 ++--
 clang/test/SemaHLSL/Semantics/position.ps.hlsl         | 2 +-
 clang/test/SemaHLSL/Semantics/target.ps.input.hlsl     | 2 +-
 6 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 712b3489207cd..1f49ebd98d6f1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13681,7 +13681,7 @@ def err_hlsl_init_priority_unsupported : Error<
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported %select{in %1 shaders"
-            "|as %1 shader %3; it is only available as %4}2">;
+            "|in %1 shader %3; it is only available as %4}2">;
 def err_hlsl_semantic_partial_explicit_indexing
     : Error<"partial explicit stage input location assignment via "
             "vk::location(X) unsupported">;
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
index 0fc3673afe3cb..279bad839ba82 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ms.input.hlsl
@@ -3,5 +3,5 @@
 
 [numthreads(1,1,1)]
 void main(uint a : A) {
-// expected-error at -1 {{semantic 'A' is not supported as mesh shader inputs; it is only available as an output, a patch constant or a primitive}}
+// expected-error at -1 {{semantic 'A' is not supported in mesh shader inputs; it is only available as an output, a patch constant or a primitive}}
 }
diff --git a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
index ac086a29e0f4d..55612cf0eba8f 100644
--- a/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
+++ b/clang/test/SemaHLSL/Semantics/arbitrary.ps.output.hlsl
@@ -2,6 +2,6 @@
 // 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 not supported as pixel shader outputs; it is only available as an input}}
+// expected-error at -1 {{semantic 'B' is not supported in pixel shader outputs; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
index ede086a68d1de..606e7fff35981 100644
--- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -11,11 +11,11 @@ void ps_out(out float4 Color : SV_Target) { Color = 0; }
 
 [shader("pixel")]
 void ps_position_out(out float4 Pos : SV_Position) { Pos = 0; }
-// expected-error at -1 {{semantic 'SV_Position' is not supported as pixel shader outputs; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported in pixel shader outputs; it is only available as an input}}
 
 [shader("compute")][numthreads(1,1,1)]
 void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; }
-// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported as compute shader outputs; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_GroupIndex' is not supported in compute shader outputs; it is only available as an input}}
 
 // Output parameters share the output signature with the return value.
 [shader("pixel")]
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
index c8d2a9db796c5..7c753ba5c1806 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : A) : SV_Position {
-// expected-error at -1 {{semantic 'SV_Position' is not supported as pixel shader outputs; it is only available as an input}}
+// expected-error at -1 {{semantic 'SV_Position' is not supported in pixel shader outputs; it is only available as an input}}
   return a;
 }
diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
index fb7761cc6faf8..02ec779527fd7 100644
--- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl
@@ -2,6 +2,6 @@
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s
 
 float4 main(float4 a : SV_Target) : SV_Target {
-// expected-error at -1 {{semantic 'SV_Target' is not supported as pixel shader inputs; it is only available as an output}}
+// expected-error at -1 {{semantic 'SV_Target' is not supported in pixel shader inputs; it is only available as an output}}
   return a;
 }



More information about the cfe-commits mailing list