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

>From 96bce6ef59dc8efb2220624aa82fa13c3f20e514 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Tue, 27 Oct 2026 22:01:12 +0000
Subject: [PATCH 1/7] [HLSL] Add support for SV_SampleIndex

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 49 ++++++++++--
 clang/lib/Sema/SemaHLSL.cpp                   |  8 +-
 .../semantics/SV_SampleIndex.ps.hlsl          | 51 ++++++++++++
 .../SemaHLSL/Semantics/sampleindex.ps.hlsl    | 57 ++++++++++++++
 .../Semantics/sampleindex.stages.hlsl         | 23 ++++++
 .../llvm/Frontend/HLSL/SemanticSignatures.h   |  2 +
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |  3 +
 llvm/lib/Frontend/HLSL/SemanticSignatures.cpp |  5 ++
 llvm/lib/Target/DirectX/DXIL.td               |  9 +++
 llvm/lib/Target/DirectX/DXILShaderFlags.cpp   |  8 +-
 .../Target/DirectX/DXILSignatureAnalysis.cpp  | 23 +++++-
 .../SemanticSignatures-sample-index.ll        | 62 +++++++++++++++
 ...semantic-signatures-sample-index-errors.ll | 78 +++++++++++++++++++
 .../ShaderFlags/unpacked-low-precision.ll     | 40 ++++++++++
 llvm/test/CodeGen/DirectX/sample_index.ll     | 28 +++++++
 .../SPIRV/semantics/sample-index.ps.ll        | 32 ++++++++
 .../HLSLSemanticSignaturePackingTest.cpp      | 15 ++++
 .../Frontend/HLSLSemanticStagesTest.cpp       | 13 +++-
 18 files changed, 492 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/SV_SampleIndex.ps.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/sampleindex.stages.hlsl
 create mode 100644 
llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
 create mode 100644 
llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index-errors.ll
 create mode 100644 
llvm/test/CodeGen/DirectX/ShaderFlags/unpacked-low-precision.ll
 create mode 100644 llvm/test/CodeGen/DirectX/sample_index.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/semantics/sample-index.ps.ll

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 6dfec8cd934580..fac0f7b850f71a 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1183,14 +1183,19 @@ static llvm::Value *createSPIRVInputLoad(
     IRBuilder<> &B, llvm::Module &M, llvm::Type *Ty, const Twine &Name,
     llvm::hlsl::SPIRVInterfaceDecoration Decoration, unsigned Operand,
     llvm::hlsl::InterpolationMode InterpMode) {
+  llvm::Type *LoadTy = Ty;
+  if (Decoration == llvm::hlsl::SPIRVInterfaceDecoration::BuiltIn &&
+      Ty->isIntegerTy() && Ty->getIntegerBitWidth() < 32)
+    LoadTy = B.getInt32Ty();
   auto *GV = new llvm::GlobalVariable(
-      M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
+      M, LoadTy, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
       /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
       llvm::GlobalVariable::GeneralDynamicTLSModel,
       /* AddressSpace */ 7, /* isExternallyInitialized= */ true);
   GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
   addSPIRVInterfaceDecorations(GV, Decoration, Operand, InterpMode);
-  return B.CreateLoad(Ty, GV);
+  llvm::Value *Value = B.CreateLoad(LoadTy, GV);
+  return LoadTy == Ty ? Value : B.CreateTrunc(Value, Ty);
 }
 
 llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad(
@@ -1345,13 +1350,24 @@ static llvm::hlsl::SemanticSignatureElement 
createSemanticSignatureElement(
   for (uint32_t I = 0, E = Shape.getNumRows(); I < E; ++I)
     SemanticIndices.push_back(FirstSemanticIndex + I);
 
+  llvm::dxbc::PSV::SemanticKind Kind = llvm::hlsl::getSemanticKind(Name);
+  llvm::dxil::ElementType CompType =
+      getSignatureComponentType(CGM, Shape.RowType);
+  // These system values may be declared as int or uint, but DXIL signatures
+  // require an unsigned component type.
+  if (Kind == llvm::dxbc::PSV::SemanticKind::SampleIndex) {
+    if (CompType == llvm::dxil::ElementType::I16)
+      CompType = llvm::dxil::ElementType::U16;
+    else if (CompType == llvm::dxil::ElementType::I32)
+      CompType = llvm::dxil::ElementType::U32;
+  }
+
   // The remaining members keep their default value and will be filled at a
   // later stage, either during packing or analysis of usage. Interpolation
   // is set by the load/store traversal after visiting each leaf.
-  return llvm::hlsl::SemanticSignatureElement(
-      SigId, Name, getSignatureComponentType(CGM, Shape.RowType),
-      llvm::hlsl::getSemanticKind(Name), SemanticIndices,
-      static_cast<uint8_t>(Shape.Cols));
+  return llvm::hlsl::SemanticSignatureElement(SigId, Name, CompType, Kind,
+                                              SemanticIndices,
+                                              
static_cast<uint8_t>(Shape.Cols));
 }
 
 llvm::Value *CGHLSLRuntime::emitDXILUserSemanticLoad(
@@ -1687,6 +1703,27 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
       return B.CreateCall(
           CGM.getIntrinsic(llvm::Intrinsic::dx_start_vertex_location));
     break;
+  case llvm::dxbc::PSV::SemanticKind::SampleIndex:
+    assert(Stage == llvm::Triple::Pixel &&
+           "SV_SampleIndex is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    if (CGM.getTarget().getTriple().isSPIRV())
+      return createSPIRVInputLoad(B, CGM.getModule(), Type,
+                                  Semantic->getAttrName()->getName(),
+                                  
llvm::hlsl::SPIRVInterfaceDecoration::BuiltIn,
+                                  /* SampleId */ 18, InterpMode);
+    if (CGM.getTarget().getTriple().isDXIL()) {
+      // The sample index is read with a dedicated operation, but an unpacked
+      // element is still added to the input signature for compatibility.
+      SemanticShape Shape =
+          getSemanticShape(CGM.getContext(), getSemanticLeafType(Decl));
+      Signature.push_back(createSemanticSignatureElement(
+          CGM, Signature.size(), Semantic, Index, Shape));
+      llvm::Value *SampleIndex =
+          B.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::dx_sample_index));
+      return B.CreateTrunc(SampleIndex, Type);
+    }
+    break;
   case llvm::dxbc::PSV::SemanticKind::StartInstanceLocation:
     assert(Stage == llvm::Triple::Vertex &&
            "SV_StartInstanceLocation is in an unavailable stage and should "
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e42f9bb21ff70f..2ce99418b2f8e1 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1015,6 +1015,7 @@ static bool isVkPipelineBuiltin(const ASTContext 
&AstContext, FunctionDecl *FD,
     return (ST == llvm::Triple::Pixel && IsInput) ||
            (ST == llvm::Triple::Vertex && !IsInput);
   case SemanticKind::Barycentrics:
+  case SemanticKind::SampleIndex:
     return ST == llvm::Triple::Pixel && IsInput;
   case SemanticKind::StartVertexLocation:
   case SemanticKind::StartInstanceLocation:
@@ -1436,6 +1437,12 @@ void SemaHLSL::diagnoseSemanticType(const Decl *D,
           << A->getAttrName() << /* scalar or vector of up to */ 1 << 4
           << /* 16 or 32 bit floating-point */ 2 << DeclaredTy;
     return;
+  case SemanticKind::SampleIndex:
+    if (DeclaredTy != T || !T->isScalarType() || !isIntUpTo32Element(Ctx, T))
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar */ 0 << 1
+          << /* 16 or 32 bit integer */ 0 << DeclaredTy;
+    return;
   case SemanticKind::InstanceID:
   case SemanticKind::ShadingRate:
     // DXIL permits U32 or U16. SPIR-V requires a 32-bit scalar per:
@@ -2253,7 +2260,6 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
   case SemanticKind::DomainLocation:
   case SemanticKind::PrimitiveID:
   case SemanticKind::GSInstanceID:
-  case SemanticKind::SampleIndex:
   case SemanticKind::IsFrontFace:
   case SemanticKind::Coverage:
   case SemanticKind::InnerCoverage:
diff --git a/clang/test/CodeGenHLSL/semantics/SV_SampleIndex.ps.hlsl 
b/clang/test/CodeGenHLSL/semantics/SV_SampleIndex.ps.hlsl
new file mode 100644
index 00000000000000..ebbaf33f79e218
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_SampleIndex.ps.hlsl
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck 
--check-prefixes=CHECK-DXIL,DXIL32 %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-pixel -x hlsl -emit-llvm 
-finclude-default-header -fnative-half-type -fnative-int16-type 
-disable-llvm-passes -DINT16 -o - %s | FileCheck 
--check-prefixes=CHECK-DXIL,DXIL16 %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck 
--check-prefix=CHECK-SPIRV %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -fnative-half-type -fnative-int16-type 
-disable-llvm-passes -DINT16 -o - %s | FileCheck --check-prefix=SPIRV16 %s
+
+#ifdef INT16
+#define TYPE int16_t
+#else
+#define TYPE int
+#endif
+
+// CHECK-SPIRV: @SV_SampleIndex = external hidden thread_local addrspace(7) 
externally_initialized constant i32, !spirv.Decorations ![[#MD_0:]]
+
+// SV_SampleIndex is read with a dedicated operation. It still adds an unpacked
+// element to the input signature that does not consume a row, so the user
+// input that follows it is packed at row 0.
+
+// CHECK-DXIL-LABEL: define void @main()
+// CHECK-DXIL: %[[SI:.*]] = call i32 @llvm.dx.sample.index()
+// DXIL32-NOT: trunc
+// DXIL16: %[[SI16:.*]] = trunc i32 %[[SI]] to i16
+// CHECK-DXIL: %[[A:.*]] = call float @llvm.dx.load.input.f32(i32 1, i32 0, i8 
0, i32 poison)
+// DXIL32: call float @_Z4mainif(i32 %[[SI]], float %[[A]])
+// DXIL16: call float @_Z4mainsf(i16 %[[SI16]], float %[[A]])
+
+// Signed declarations use an unsigned DXIL component type, with 
nointerpolation.
+// DXIL32-DAG: !{i32 0, !"SV_SampleIndex", i32 5, i32 12, ![[#ZERO:]], i32 1, 
i32 1, i8 1, i32 -1, i8 -1, i8 0, i8 0, i32 0}
+// DXIL16-DAG: !{i32 0, !"SV_SampleIndex", i32 3, i32 12, ![[#ZERO:]], i32 1, 
i32 1, i8 1, i32 -1, i8 -1, i8 0, i8 0, i32 0}
+// CHECK-DXIL-DAG: !{i32 1, !"A", i32 9, i32 0, ![[#ZERO]], i32 2, i32 1, i8 
1, i32 0, i8 0, i8 0, i8 0, i32 0}
+// CHECK-DXIL-DAG: ![[#ZERO]] = !{i32 0}
+
+// CHECK-SPIRV-LABEL: define void @main()
+// CHECK-SPIRV: %[[SI:.*]] = load i32, ptr addrspace(7) @SV_SampleIndex, align 
4
+// CHECK-SPIRV: call spir_func float @_Z4mainif(i32 %[[SI]],
+float main(TYPE s : SV_SampleIndex, float a : A) : SV_Target {
+  return a * s;
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#BUILTIN:]], ![[#FLAT:]]}
+// The metadata below contains SPIR-V decoration 'BuiltIn' followed by
+// BuiltIn SampleId.
+// CHECK-SPIRV-DAG: ![[#BUILTIN]] = !{i32 11, i32 18}
+// CHECK-SPIRV-DAG: ![[#FLAT]] = !{i32 14}
+
+// The SPIR-V built-in is a 32-bit integer, so a 16-bit declaration is
+// truncated.
+// SPIRV16: @SV_SampleIndex = {{.*}} addrspace(7) externally_initialized 
constant i32,
+// SPIRV16-LABEL: define void @main()
+// SPIRV16: %[[V32:.*]] = load i32, ptr addrspace(7) @SV_SampleIndex
+// SPIRV16: %[[V16:.*]] = trunc i32 %[[V32]] to i16
+// SPIRV16: call spir_func float @_Z4mainsf(i16 %[[V16]],
diff --git a/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl 
b/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
new file mode 100644
index 00000000000000..520893b5c4d9e1
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type 
-fnative-int16-type -finclude-default-header -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -fnative-half-type 
-fnative-int16-type -finclude-default-header -fsyntax-only -verify %s
+
+// Type, index and interpolation checks for SV_SampleIndex. These are
+// independent of the shader stage and IO direction, which are covered by
+// sampleindex.stages.hlsl.
+
+[shader("pixel")]
+void valid_uint(uint s : SV_SampleIndex) {}
+[shader("pixel")]
+void valid_int(int s : sv_sampleindex0) {}
+[shader("pixel")]
+void valid_flat(nointerpolation uint s : SV_SampleIndex) {}
+
+// 16-bit integers are allowed. SPIR-V converts them to and from the 32-bit
+// built-in.
+[shader("pixel")]
+void uint16(uint16_t s : SV_SampleIndex) {}
+[shader("pixel")]
+void int16(int16_t s : SV_SampleIndex) {}
+
+struct Input {
+  float4 p : SV_Position;
+  uint s : SV_SampleIndex;
+};
+[shader("pixel")]
+void valid_struct(Input i) {}
+
+[shader("pixel")]
+void bad_float(float s : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' must be a scalar of 16 or 32 
bit integer type (was 'float')}}
+[shader("pixel")]
+void bad_bool(bool s : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' must be a scalar of 16 or 32 
bit integer type (was 'bool')}}
+[shader("pixel")]
+void bad_int64(uint64_t s : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' must be a scalar of 16 or 32 
bit integer type (was 'uint64_t' (aka 'unsigned long'))}}
+[shader("pixel")]
+void bad_vector(uint1 s : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' must be a scalar of 16 or 32 
bit integer type (was 'uint1' (aka 'vector<uint, 1>'))}}
+[shader("pixel")]
+void bad_array(uint s[1] : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' must be a scalar of 16 or 32 
bit integer type (was 'uint[1]'}}
+
+[shader("pixel")]
+void bad_index(uint s : SV_SampleIndex1) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' does not allow indexing}}
+
+[shader("pixel")]
+void same_index(uint s : SV_SampleIndex, // expected-note {{previous use is 
here}}
+                uint t : SV_SampleIndex0) {} // expected-note {{'t' declared 
here}}
+// expected-error@-1 {{semantic index overlap SV_SampleIndex0}}
+
+// Integer inputs can only use nointerpolation.
+[shader("pixel")]
+void bad_interpolation(sample uint s : SV_SampleIndex) {}
+// expected-error@-1 {{interpolation modifiers other than 'nointerpolation' 
cannot be used with type 'uint' (aka 'unsigned int')}}
diff --git a/clang/test/SemaHLSL/Semantics/sampleindex.stages.hlsl 
b/clang/test/SemaHLSL/Semantics/sampleindex.stages.hlsl
new file mode 100644
index 00000000000000..059328ccaf14ed
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/sampleindex.stages.hlsl
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library 
-finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header 
-x hlsl -verify -o - %s
+
+// Stage and IO direction checks for SV_SampleIndex: only a pixel input.
+// Type, index and interpolation checks are covered by sampleindex.ps.hlsl.
+
+[shader("pixel")]
+float4 ok_ps_in(uint s : SV_SampleIndex) : SV_Target {
+  return float4((float)s, 0, 0, 1);
+}
+
+[shader("pixel")]
+uint bad_ps_out() : SV_SampleIndex { return 0; }
+// expected-error@-1 {{semantic 'SV_SampleIndex' is not supported in pixel 
shader outputs}}
+
+[shader("vertex")]
+float4 bad_vs_in(uint s : SV_SampleIndex) : SV_Position { return 0; }
+// expected-error@-1 {{semantic 'SV_SampleIndex' is not supported in vertex 
shader inputs}}
+
+[shader("compute")]
+[numthreads(1, 1, 1)]
+void bad_cs_in(uint s : SV_SampleIndex) {}
+// expected-error@-1 {{semantic 'SV_SampleIndex' is not supported in compute 
shader inputs}}
diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h 
b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 90b7241d4d21d4..971b493ab7ee36 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -110,6 +110,8 @@ LLVM_ABI dxbc::PSV::InterpolationMode 
normalizeInterpolationMode(
 enum class SemanticInterpretation {
   Invalid,
   NotAllocated,
+  // In the signature, but not packed. This includes DXC's Shadow
+  // interpretation, whose value is read with a dedicated intrinsic.
   NotPacked,
   Arbitrary,
   SV,
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index b9b02aeb081916..6bde73d87f356a 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -607,4 +607,7 @@ let TargetPrefix = "dx" in {
 
   def int_dx_start_instance_location
       : DefaultAttrsIntrinsic<[llvm_i32_ty], [], [IntrNoMem]>;
+
+  def int_dx_sample_index
+      : DefaultAttrsIntrinsic<[llvm_i32_ty], [], [IntrNoMem]>;
 }
diff --git a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp 
b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
index 114d6309b12ddb..85c016b261ecd4 100644
--- a/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
+++ b/llvm/lib/Frontend/HLSL/SemanticSignatures.cpp
@@ -180,6 +180,11 @@ hlsl::getAvailableStages(dxbc::PSV::SemanticKind 
SemanticKind) {
         {Triple::Vertex, IOType::In, SemanticInterpretation::NotAllocated}};
     return Stages;
   }
+  case dxbc::PSV::SemanticKind::SampleIndex: {
+    static constexpr SemanticStageInfo Stages[] = {
+        {Triple::Pixel, IOType::In, SemanticInterpretation::NotPacked}};
+    return Stages;
+  }
   case dxbc::PSV::SemanticKind::Barycentrics: {
     static constexpr SemanticStageInfo Stages[] = {
         {Triple::Pixel, IOType::In, SemanticInterpretation::NotPacked}};
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 3f81e83b01ecba..ca8dd5450bff6d 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -1153,6 +1153,15 @@ def DerivFineY : DXILOp<86, unary> {
   let stages = [Stages<DXIL1_0, [library, pixel]>];
 }
 
+def SampleIndex : DXILOp<90, sampleIndex> {
+  let Doc = "returns the sample index in a sample-frequency pixel shader";
+  let intrinsics = [IntrinSelect<int_dx_sample_index>];
+  let result = OverloadTy;
+  let overloads = [Overloads<DXIL1_0, [Int32Ty]>];
+  let stages = [Stages<DXIL1_0, [pixel]>];
+  let attributes = [Attributes<DXIL1_0, [ReadNone]>];
+}
+
 def ThreadId : DXILOp<93, threadId> {
   let Doc = "Reads the thread ID";
   let intrinsics = [IntrinSelect<int_dx_thread_id>];
diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp 
b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
index 59953c191497be..b61f0aca42773b 100644
--- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
+++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
@@ -456,11 +456,13 @@ void ModuleShaderFlags::initialize(Module &M, 
DXILResourceTypeMap &DRTM,
         CSF.Barycentrics |= llvm::any_of(Sig->Inputs, [](const auto &E) {
           return E.SemanticKind == dxbc::PSV::SemanticKind::Barycentrics;
         });
+        // Unallocated elements do not occupy signature registers. Their type
+        // requirements come from the instructions that access them.
         auto HasLowPrecision = [](const auto &Elements) {
           return llvm::any_of(Elements, [](const auto &E) {
-            return E.CompType == ElementType::F16 ||
-                   E.CompType == ElementType::I16 ||
-                   E.CompType == ElementType::U16;
+            return E.isAllocated() && (E.CompType == ElementType::F16 ||
+                                       E.CompType == ElementType::I16 ||
+                                       E.CompType == ElementType::U16);
           });
         };
         if (HasLowPrecision(Sig->Inputs) || HasLowPrecision(Sig->Outputs)) {
diff --git a/llvm/lib/Target/DirectX/DXILSignatureAnalysis.cpp 
b/llvm/lib/Target/DirectX/DXILSignatureAnalysis.cpp
index d652ac24321fb7..7fa66a249ba8d5 100644
--- a/llvm/lib/Target/DirectX/DXILSignatureAnalysis.cpp
+++ b/llvm/lib/Target/DirectX/DXILSignatureAnalysis.cpp
@@ -96,6 +96,12 @@ static Error diagnoseSemanticType(const 
SemanticSignatureElement &E) {
       return signatureError(
           "SV_ShadingRate requires a scalar 16- or 32-bit unsigned integer");
     break;
+  case dxbc::PSV::SemanticKind::SampleIndex:
+    if (E.Rows != 1 || E.Cols != 1 ||
+        (E.CompType != ElementType::U16 && E.CompType != ElementType::U32))
+      return signatureError(
+          "SV_SampleIndex requires a scalar 16- or 32-bit unsigned integer");
+    break;
   case dxbc::PSV::SemanticKind::Barycentrics:
     if (E.Rows != 1 || E.Cols != 3 ||
         (E.CompType != ElementType::F16 && E.CompType != ElementType::F32))
@@ -123,6 +129,10 @@ static Error diagnoseSemanticIndex(const 
SemanticSignatureElement &E) {
     if (E.SemanticIndices.front() != 0)
       return signatureError("SV_Barycentrics requires semantic index 0");
     break;
+  case dxbc::PSV::SemanticKind::SampleIndex:
+    if (E.SemanticIndices.front() != 0)
+      return signatureError("SV_SampleIndex requires semantic index 0");
+    break;
   default:
     break;
   }
@@ -199,8 +209,8 @@ static Error parseSignature(Metadata *MD,
                             "point");
     if (Interpretation == SemanticInterpretation::NotPacked) {
       if (E.isAllocated())
-        return signatureError(
-            "SV_Barycentrics must not have an allocated location");
+        return signatureError(E.SemanticName +
+                              " must not have an allocated location");
     } else {
       if (!E.isAllocated())
         return signatureError("signature element " + Twine(E.SigId) + " ('" +
@@ -301,6 +311,8 @@ static Error analyzeAccess(const IntrinsicInst &I, 
EntrySignature &Sig) {
     return signatureError(Category + " signature element " + Twine(E.SigId) +
                           " ('" + E.SemanticName + "'): " + Message);
   };
+  if (E.SemanticKind == dxbc::PSV::SemanticKind::SampleIndex)
+    return AccessError("must not be read with dx.op.load/store");
   Type *Ty = Input ? I.getType() : I.getArgOperand(3)->getType();
   unsigned Width = 1;
   if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
@@ -532,6 +544,8 @@ static dxbc::D3DSystemValue 
getSystemValue(dxbc::PSV::SemanticKind Kind) {
     return dxbc::D3DSystemValue::ShadingRate;
   case KindTy::Barycentrics:
     return dxbc::D3DSystemValue::Barycentrics;
+  case KindTy::SampleIndex:
+    return dxbc::D3DSystemValue::SampleIndex;
   default:
     llvm_unreachable("validated vertex/pixel signature semantic");
   }
@@ -564,6 +578,8 @@ static StringRef getContainerSemanticName(const 
SemanticSignatureElement &E) {
     return "SV_ShadingRate";
   case Kind::Barycentrics:
     return "SV_Barycentrics";
+  case Kind::SampleIndex:
+    return "SV_SampleIndex";
   default:
     llvm_unreachable("validated vertex/pixel signature semantic");
   }
@@ -643,7 +659,8 @@ void EntrySignature::updatePSV(mcdxbc::PSVRuntimeInfo &PSV,
         llvm::any_of(Inputs, [](const auto &E) {
           return E.InterpMode == dxbc::PSV::InterpolationMode::LinearSample ||
                  E.InterpMode ==
-                     dxbc::PSV::InterpolationMode::LinearNoperspectiveSample;
+                     dxbc::PSV::InterpolationMode::LinearNoperspectiveSample ||
+                 E.SemanticKind == dxbc::PSV::SemanticKind::SampleIndex;
         });
 }
 
diff --git 
a/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll 
b/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
new file mode 100644
index 00000000000000..72a23895e039ff
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
@@ -0,0 +1,62 @@
+; RUN: opt -disable-output -passes='print<dxil-signature>' %s 2>&1 | FileCheck 
%s --check-prefix=ANALYSIS
+; RUN: llc -filetype=obj %s -o %t.dxbc
+; RUN: obj2yaml %t.dxbc | FileCheck %s --check-prefix=PARTS
+; RUN: llvm-objcopy --dump-section=DXIL=%t.bc %t.dxbc
+; RUN: llvm-dis %t.bc -o - | FileCheck %s --check-prefixes=MD,DXIL
+
+; SV_SampleIndex is an unpacked element: it keeps a signature ID, but is read 
with
+; dx.op.sampleIndex, does not consume a row and marks the pixel shader as
+; sample-frequency.
+target triple = "dxil-pc-shadermodel6.0-pixel"
+
+define void @main() #0 {
+  %p = call float @llvm.dx.load.input.f32(i32 0, i32 0, i8 0, i32 poison)
+  %si = call i32 @llvm.dx.sample.index()
+  %f = uitofp i32 %si to float
+  %result = fadd float %p, %f
+  call void @llvm.dx.store.output.f32(i32 0, i32 0, i8 0, float %result)
+  ret void
+}
+attributes #0 = { "hlsl.shader"="pixel" }
+
+!dx.valver = !{!10}
+!10 = !{i32 1, i32 8}
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, !2}
+!1 = !{!3, !4}
+!2 = !{!6}
+!3 = !{i32 0, !"A", i32 9, i32 0, !7, i32 2, i32 1, i8 1, i32 0, i8 0, i8 0, 
i8 0, i32 0}
+!4 = !{i32 1, !"SV_SampleIndex", i32 5, i32 12, !7, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!6 = !{i32 0, !"SV_Target", i32 9, i32 16, !7, i32 0, i32 1, i8 1, i32 0, i8 
0, i8 0, i8 0, i32 0}
+!7 = !{i32 0}
+
+; ANALYSIS:      Inputs: 2 elements, 1 vectors
+; ANALYSIS-NEXT: 0: A rows=1 cols=1 at 0:0 usage=1 dynamic=0
+; ANALYSIS-NEXT: 1: SV_SampleIndex rows=1 cols=1 at 4294967295:255 usage=0 
dynamic=0
+; ANALYSIS-NEXT: Outputs: 1 elements, 1 vectors
+
+; DXIL: call float @dx.op.loadInput.f32(i32 4, i32 0, i32 0, i8 0, i32 {{.*}})
+; DXIL: call i32 @dx.op.sampleIndex.i32(i32 90)
+; MD-DAG: !{i32 1, !"SV_SampleIndex", i8 5, i8 12, !{{[0-9]+}}, i8 1, i32 1, 
i8 1, i32 -1, i8 -1, null}
+
+; PARTS: Name: ISG1
+; PARTS:      Name: SV_SampleIndex
+; PARTS-NEXT: Index: 0
+; PARTS-NEXT: SystemValue: SampleIndex
+; PARTS-NEXT: CompType: UInt32
+; PARTS-NEXT: Register: 4294967295
+; PARTS-NEXT: Mask: 1
+; PARTS-NEXT: ExclusiveMask: 0
+; PARTS: Name: PSV0
+; PARTS: SampleFrequency: 1
+; PARTS:      SigInputVectors: 1
+; PARTS: SigInputElements:
+; PARTS:      Kind: Arbitrary
+; PARTS:      Indices: [ 0 ]
+; PARTS-NEXT: StartRow: 0
+; PARTS-NEXT: Cols: 1
+; PARTS-NEXT: StartCol: 0
+; PARTS-NEXT: Allocated: false
+; PARTS-NEXT: Kind: SampleIndex
+; PARTS-NEXT: ComponentType: UInt32
+; PARTS-NEXT: Interpolation: Constant
diff --git 
a/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index-errors.ll 
b/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index-errors.ll
new file mode 100644
index 00000000000000..9d4d63bbc97289
--- /dev/null
+++ 
b/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index-errors.ll
@@ -0,0 +1,78 @@
+; RUN: split-file %s %t
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/signed.ll 
2>&1 | FileCheck %s --check-prefix=CHECK-SIGNED
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/cols.ll 2>&1 
| FileCheck %s --check-prefix=CHECK-COLS
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/index.ll 
2>&1 | FileCheck %s --check-prefix=CHECK-INDEX
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/allocated.ll 
2>&1 | FileCheck %s --check-prefix=CHECK-ALLOCATED
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/load.ll 2>&1 
| FileCheck %s --check-prefix=CHECK-LOAD
+; RUN: not opt -disable-output -passes=dxil-translate-metadata %t/vertex.ll 
2>&1 | FileCheck %s --check-prefix=CHECK-VERTEX
+
+; Each case is a complete module with one invalid SV_SampleIndex declaration.
+
+;--- signed.ll
+; CHECK-SIGNED: SV_SampleIndex requires a scalar 16- or 32-bit unsigned integer
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define void @main() #0 { ret void }
+attributes #0 = { "hlsl.shader"="pixel" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 4, i32 12, !3, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
+
+;--- cols.ll
+; CHECK-COLS: SV_SampleIndex requires a scalar 16- or 32-bit unsigned integer
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define void @main() #0 { ret void }
+attributes #0 = { "hlsl.shader"="pixel" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 5, i32 12, !3, i32 1, i32 1, i8 2, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
+
+;--- index.ll
+; CHECK-INDEX: SV_SampleIndex requires semantic index 0
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define void @main() #0 { ret void }
+attributes #0 = { "hlsl.shader"="pixel" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 5, i32 12, !3, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 1}
+
+;--- allocated.ll
+; CHECK-ALLOCATED: SV_SampleIndex must not have an allocated location
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define void @main() #0 { ret void }
+attributes #0 = { "hlsl.shader"="pixel" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 5, i32 12, !3, i32 1, i32 1, i8 1, i32 0, 
i8 0, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
+
+;--- load.ll
+; CHECK-LOAD: input signature element 0 ('SV_SampleIndex'): must not be read 
with dx.op.load/store
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define void @main() #0 {
+  %si = call i32 @llvm.dx.load.input.i32(i32 0, i32 0, i8 0, i32 poison)
+  ret void
+}
+attributes #0 = { "hlsl.shader"="pixel" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 5, i32 12, !3, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
+
+;--- vertex.ll
+; CHECK-VERTEX: invalid or unsupported semantic for this stage and signature 
category
+target triple = "dxil-pc-shadermodel6.0-vertex"
+define void @main() #0 { ret void }
+attributes #0 = { "hlsl.shader"="vertex" }
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 5, i32 12, !3, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/unpacked-low-precision.ll 
b/llvm/test/CodeGen/DirectX/ShaderFlags/unpacked-low-precision.ll
new file mode 100644
index 00000000000000..3e7eebefbb40bb
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ShaderFlags/unpacked-low-precision.ll
@@ -0,0 +1,40 @@
+; RUN: opt -disable-output -passes=print-dx-shader-flags %s 2>&1 | FileCheck %s
+
+; Unallocated (NotPacked) elements do not occupy signature registers, so only
+; the instructions that read them contribute type requirements, as in DXC. A
+; 16-bit SV_SampleIndex is read with the 32-bit dx.op.sampleIndex, and an
+; unused 16-bit SV_Barycentrics has no loadInput. Allocated 16-bit signature
+; elements still require low-precision support.
+
+target triple = "dxil-pc-shadermodel6.2-library"
+
+; CHECK: ; Shader Flags Value: 0x00400020
+; CHECK: Function sample_index : 0x00000000
+; CHECK: Function barycentrics : 0x00400000
+; CHECK: Function arbitrary : 0x00000020
+define void @sample_index() #0 {
+entry:
+  %0 = call i32 @llvm.dx.sample.index()
+  ret void
+}
+define void @barycentrics() #0 {
+entry:
+  ret void
+}
+define void @arbitrary() #0 {
+entry:
+  ret void
+}
+attributes #0 = { "hlsl.shader"="pixel" }
+
+!dx.semantic.signatures = !{!0, !4, !7}
+!0 = !{ptr @sample_index, !1, null}
+!1 = !{!2}
+!2 = !{i32 0, !"SV_SampleIndex", i32 3, i32 12, !3, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!3 = !{i32 0}
+!4 = !{ptr @barycentrics, !5, null}
+!5 = !{!6}
+!6 = !{i32 0, !"SV_Barycentrics", i32 8, i32 28, !3, i32 2, i32 1, i8 3, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!7 = !{ptr @arbitrary, !8, null}
+!8 = !{!9}
+!9 = !{i32 0, !"A", i32 3, i32 0, !3, i32 1, i32 1, i8 1, i32 0, i8 0, i8 0, 
i8 0, i32 0}
diff --git a/llvm/test/CodeGen/DirectX/sample_index.ll 
b/llvm/test/CodeGen/DirectX/sample_index.ll
new file mode 100644
index 00000000000000..0119d427ff3878
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/sample_index.ll
@@ -0,0 +1,28 @@
+; RUN: split-file %s %t
+; RUN: opt -S -dxil-op-lower %t/pixel.ll | FileCheck %s
+; RUN: not opt -S -dxil-op-lower %t/vertex.ll 2>&1 | FileCheck %s 
--check-prefix=STAGE
+
+; CHECK-LABEL: define i32 @main(
+; CHECK: %[[SI:.*]] = call i32 @dx.op.sampleIndex.i32(i32 90)
+; CHECK: ret i32 %[[SI]]
+; CHECK: declare i32 @dx.op.sampleIndex.i32(i32) #[[#ATTR:]]
+; CHECK: attributes #[[#ATTR]] = { nounwind memory(none) }
+
+; SampleIndex is only available to pixel shaders.
+; STAGE: Cannot create SampleIndex operation: Invalid stage
+
+;--- pixel.ll
+target triple = "dxil-pc-shadermodel6.0-pixel"
+define i32 @main() {
+entry:
+  %0 = call i32 @llvm.dx.sample.index()
+  ret i32 %0
+}
+
+;--- vertex.ll
+target triple = "dxil-pc-shadermodel6.0-vertex"
+define i32 @main() {
+entry:
+  %0 = call i32 @llvm.dx.sample.index()
+  ret i32 %0
+}
diff --git a/llvm/test/CodeGen/SPIRV/semantics/sample-index.ps.ll 
b/llvm/test/CodeGen/SPIRV/semantics/sample-index.ps.ll
new file mode 100644
index 00000000000000..ecc6bbce22c0df
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/semantics/sample-index.ps.ll
@@ -0,0 +1,32 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan1.3-pixel %s 
-o - | FileCheck %s --implicit-check-not=OpExtension
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-pixel %s -o 
- -filetype=obj | spirv-val --target-env vulkan1.3 %}
+
+; SV_SampleIndex is lowered to a flat load of the SampleId built-in, which
+; requires SampleRateShading.
+
+; CHECK-DAG: OpCapability SampleRateShading
+; CHECK-DAG: OpDecorate %[[#SAMPLE:]] BuiltIn SampleId
+; CHECK-DAG: OpDecorate %[[#SAMPLE]] Flat
+; CHECK-DAG: %[[#INT:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#PTR:]] = OpTypePointer Input %[[#INT]]
+; CHECK-DAG: %[[#SAMPLE]] = OpVariable %[[#PTR]] Input
+
+@SV_SampleIndex = external hidden thread_local addrspace(7) 
externally_initialized constant i32, !spirv.Decorations !0
+@SV_Target0 = external hidden thread_local addrspace(8) global float, 
!spirv.Decorations !3
+
+define void @main() #0 {
+entry:
+; CHECK: OpLoad %[[#INT]] %[[#SAMPLE]]
+  %0 = load i32, ptr addrspace(7) @SV_SampleIndex, align 4
+  %1 = uitofp i32 %0 to float
+  store float %1, ptr addrspace(8) @SV_Target0, align 4
+  ret void
+}
+
+attributes #0 = { "hlsl.shader"="pixel" }
+
+!0 = !{!1, !2}
+!1 = !{i32 11, i32 18}
+!2 = !{i32 14}
+!3 = !{!4}
+!4 = !{i32 30, i32 0}
diff --git a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp 
b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
index 080a675c6e763a..94af5531592c95 100644
--- a/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticSignaturePackingTest.cpp
@@ -258,6 +258,21 @@ TEST_F(HLSLSemanticSignaturePackingTest, 
BarycentricsDoNotReserveRegisters) {
                   {Unallocated, {/*Row=*/0, /*Col=*/0}});
 }
 
+TEST_F(HLSLSemanticSignaturePackingTest, SampleIndexDoesNotReserveRegisters) {
+  // SV_SampleIndex stays in the signature but is read with a dedicated
+  // intrinsic and never occupies a register.
+  TestConfig Config(
+      Triple::Pixel, IOType::In,
+      {{dxbc::PSV::SemanticKind::SampleIndex, /*Rows=*/1, /*Cols=*/1,
+        dxil::ElementType::U32, dxbc::PSV::InterpolationMode::Constant},
+       {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/32, /*Cols=*/4,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+  for (PackingMethod Method :
+       {PackingMethod::PrefixStable, PackingMethod::Optimized})
+    verifyPacking(Method, Config, /*ExpectedRows=*/32,
+                  {Unallocated, {/*Row=*/0, /*Col=*/0}});
+}
+
 TEST_F(HLSLSemanticSignaturePackingTest, StacksInDeclarationOrder) {
   // Elements are assigned whole rows in declaration order, regardless of their
   // semantic interpretation.
diff --git a/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp 
b/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp
index cd4a57abb68a6a..399e6630a189e1 100644
--- a/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp
+++ b/llvm/unittests/Frontend/HLSLSemanticStagesTest.cpp
@@ -76,7 +76,8 @@ constexpr SemanticKind SupportedKinds[] = 
{SemanticKind::Arbitrary,
                                            SemanticKind::ShadingRate,
                                            SemanticKind::Barycentrics,
                                            SemanticKind::StartVertexLocation,
-                                           
SemanticKind::StartInstanceLocation};
+                                           SemanticKind::StartInstanceLocation,
+                                           SemanticKind::SampleIndex};
 
 // Ensure all stages return a valid SemanticInterpretation
 TEST(HLSLSemanticStagesTest, StagesAreValid) {
@@ -191,4 +192,14 @@ TEST(HLSLSemanticStagesTest, 
StartInstanceLocationSignaturePoints) {
   expectOnlySignaturePoints(SemanticKind::StartInstanceLocation, Valid);
 }
 
+// SV_SampleIndex is read with a dedicated operation, but is still added to the
+// pixel input signature for compatibility. Valid only on pixel inputs, where 
it
+// is in the signature but not packed.
+TEST(HLSLSemanticStagesTest, SampleIndexSignaturePoints) {
+  const SignaturePoint Valid[] = {
+      {Triple::Pixel, IOType::In, SemanticInterpretation::NotPacked},
+  };
+  expectOnlySignaturePoints(SemanticKind::SampleIndex, Valid);
+}
+
 } // namespace

>From df5731068e66f7bc6d25a83d4a26a7bf09bfec74 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 17:30:03 +0000
Subject: [PATCH 2/7] review: simplify the SV_SampleIndex scalar type check

---
 clang/lib/Sema/SemaHLSL.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2ce99418b2f8e1..785ffea50d875e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1438,7 +1438,7 @@ void SemaHLSL::diagnoseSemanticType(const Decl *D,
           << /* 16 or 32 bit floating-point */ 2 << DeclaredTy;
     return;
   case SemanticKind::SampleIndex:
-    if (DeclaredTy != T || !T->isScalarType() || !isIntUpTo32Element(Ctx, T))
+    if (!DeclaredTy->isScalarType() || !isIntUpTo32Element(Ctx, DeclaredTy))
       Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
           << A->getAttrName() << /* scalar */ 0 << 1
           << /* 16 or 32 bit integer */ 0 << DeclaredTy;

>From 8f51c419f8d964bebe45443db3e6894eb9a2735b Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 17:30:03 +0000
Subject: [PATCH 3/7] review: drop the unneeded SV_Position from the
 SV_SampleIndex struct test

---
 clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl 
b/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
index 520893b5c4d9e1..63bb531c15e767 100644
--- a/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/sampleindex.ps.hlsl
@@ -20,7 +20,6 @@ void uint16(uint16_t s : SV_SampleIndex) {}
 void int16(int16_t s : SV_SampleIndex) {}
 
 struct Input {
-  float4 p : SV_Position;
   uint s : SV_SampleIndex;
 };
 [shader("pixel")]

>From c27905f1e7b5af3a2c7e3826d8a9c475f5104d0e Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 17:30:03 +0000
Subject: [PATCH 4/7] review: remove the DXC-specific NotPacked comment

---
 llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h 
b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
index 971b493ab7ee36..90b7241d4d21d4 100644
--- a/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
+++ b/llvm/include/llvm/Frontend/HLSL/SemanticSignatures.h
@@ -110,8 +110,6 @@ LLVM_ABI dxbc::PSV::InterpolationMode 
normalizeInterpolationMode(
 enum class SemanticInterpretation {
   Invalid,
   NotAllocated,
-  // In the signature, but not packed. This includes DXC's Shadow
-  // interpretation, whose value is read with a dedicated intrinsic.
   NotPacked,
   Arbitrary,
   SV,

>From 90e84055c60da2acb4a762b18397893fcf5c56f7 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 21:30:57 +0000
Subject: [PATCH 5/7] review: split the SV_SampleIndex container test into
 packing and container part tests

---
 .../SemanticSignatures-sample-index.ll        | 63 ++++++++-----------
 .../semantic-signatures-sample-index.ll       | 37 +++++++++++
 2 files changed, 63 insertions(+), 37 deletions(-)
 create mode 100644 
llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index.ll

diff --git 
a/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll 
b/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
index 72a23895e039ff..f91673596445ee 100644
--- a/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
+++ b/llvm/test/CodeGen/DirectX/ContainerData/SemanticSignatures-sample-index.ll
@@ -1,12 +1,10 @@
-; RUN: opt -disable-output -passes='print<dxil-signature>' %s 2>&1 | FileCheck 
%s --check-prefix=ANALYSIS
 ; RUN: llc -filetype=obj %s -o %t.dxbc
-; RUN: obj2yaml %t.dxbc | FileCheck %s --check-prefix=PARTS
-; RUN: llvm-objcopy --dump-section=DXIL=%t.bc %t.dxbc
-; RUN: llvm-dis %t.bc -o - | FileCheck %s --check-prefixes=MD,DXIL
+; RUN: obj2yaml %t.dxbc | FileCheck %s
 
-; SV_SampleIndex is an unpacked element: it keeps a signature ID, but is read 
with
-; dx.op.sampleIndex, does not consume a row and marks the pixel shader as
-; sample-frequency.
+; SV_SampleIndex is written to the ISG1 and PSV0 parts as an unallocated input
+; element, and marks the pixel shader as sample-frequency. Packing is covered 
by
+; Metadata/semantic-signatures-sample-index.ll and the dx.op.sampleIndex
+; lowering by sample_index.ll.
 target triple = "dxil-pc-shadermodel6.0-pixel"
 
 define void @main() #0 {
@@ -30,33 +28,24 @@ attributes #0 = { "hlsl.shader"="pixel" }
 !6 = !{i32 0, !"SV_Target", i32 9, i32 16, !7, i32 0, i32 1, i8 1, i32 0, i8 
0, i8 0, i8 0, i32 0}
 !7 = !{i32 0}
 
-; ANALYSIS:      Inputs: 2 elements, 1 vectors
-; ANALYSIS-NEXT: 0: A rows=1 cols=1 at 0:0 usage=1 dynamic=0
-; ANALYSIS-NEXT: 1: SV_SampleIndex rows=1 cols=1 at 4294967295:255 usage=0 
dynamic=0
-; ANALYSIS-NEXT: Outputs: 1 elements, 1 vectors
-
-; DXIL: call float @dx.op.loadInput.f32(i32 4, i32 0, i32 0, i8 0, i32 {{.*}})
-; DXIL: call i32 @dx.op.sampleIndex.i32(i32 90)
-; MD-DAG: !{i32 1, !"SV_SampleIndex", i8 5, i8 12, !{{[0-9]+}}, i8 1, i32 1, 
i8 1, i32 -1, i8 -1, null}
-
-; PARTS: Name: ISG1
-; PARTS:      Name: SV_SampleIndex
-; PARTS-NEXT: Index: 0
-; PARTS-NEXT: SystemValue: SampleIndex
-; PARTS-NEXT: CompType: UInt32
-; PARTS-NEXT: Register: 4294967295
-; PARTS-NEXT: Mask: 1
-; PARTS-NEXT: ExclusiveMask: 0
-; PARTS: Name: PSV0
-; PARTS: SampleFrequency: 1
-; PARTS:      SigInputVectors: 1
-; PARTS: SigInputElements:
-; PARTS:      Kind: Arbitrary
-; PARTS:      Indices: [ 0 ]
-; PARTS-NEXT: StartRow: 0
-; PARTS-NEXT: Cols: 1
-; PARTS-NEXT: StartCol: 0
-; PARTS-NEXT: Allocated: false
-; PARTS-NEXT: Kind: SampleIndex
-; PARTS-NEXT: ComponentType: UInt32
-; PARTS-NEXT: Interpolation: Constant
+; CHECK: Name: ISG1
+; CHECK:      Name: SV_SampleIndex
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: SystemValue: SampleIndex
+; CHECK-NEXT: CompType: UInt32
+; CHECK-NEXT: Register: 4294967295
+; CHECK-NEXT: Mask: 1
+; CHECK-NEXT: ExclusiveMask: 0
+; CHECK: Name: PSV0
+; CHECK: SampleFrequency: 1
+; CHECK:      SigInputVectors: 1
+; CHECK: SigInputElements:
+; CHECK:      Kind: Arbitrary
+; CHECK:      Indices: [ 0 ]
+; CHECK-NEXT: StartRow: 0
+; CHECK-NEXT: Cols: 1
+; CHECK-NEXT: StartCol: 0
+; CHECK-NEXT: Allocated: false
+; CHECK-NEXT: Kind: SampleIndex
+; CHECK-NEXT: ComponentType: UInt32
+; CHECK-NEXT: Interpolation: Constant
diff --git 
a/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index.ll 
b/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index.ll
new file mode 100644
index 00000000000000..f67eefd656662e
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Metadata/semantic-signatures-sample-index.ll
@@ -0,0 +1,37 @@
+; RUN: opt -disable-output -passes='print<dxil-signature>' %s 2>&1 | FileCheck 
%s --check-prefix=ANALYSIS
+; RUN: opt -S -dxil-translate-metadata %s | FileCheck %s --check-prefix=MD
+
+; SV_SampleIndex is an unpacked input element: it keeps signature ID 1, but is
+; not allocated a row or column, so the arbitrary input A is still packed at
+; row 0.
+target triple = "dxil-pc-shadermodel6.0-pixel"
+
+define void @main() #0 {
+  %p = call float @llvm.dx.load.input.f32(i32 0, i32 0, i8 0, i32 poison)
+  %si = call i32 @llvm.dx.sample.index()
+  %f = uitofp i32 %si to float
+  %result = fadd float %p, %f
+  call void @llvm.dx.store.output.f32(i32 0, i32 0, i8 0, float %result)
+  ret void
+}
+attributes #0 = { "hlsl.shader"="pixel" }
+
+!dx.valver = !{!10}
+!10 = !{i32 1, i32 8}
+!dx.semantic.signatures = !{!0}
+!0 = !{ptr @main, !1, !2}
+!1 = !{!3, !4}
+!2 = !{!6}
+!3 = !{i32 0, !"A", i32 9, i32 0, !7, i32 2, i32 1, i8 1, i32 0, i8 0, i8 0, 
i8 0, i32 0}
+!4 = !{i32 1, !"SV_SampleIndex", i32 5, i32 12, !7, i32 1, i32 1, i8 1, i32 
-1, i8 -1, i8 0, i8 0, i32 0}
+!6 = !{i32 0, !"SV_Target", i32 9, i32 16, !7, i32 0, i32 1, i8 1, i32 0, i8 
0, i8 0, i8 0, i32 0}
+!7 = !{i32 0}
+
+; ANALYSIS:      Inputs: 2 elements, 1 vectors
+; ANALYSIS-NEXT: 0: A rows=1 cols=1 at 0:0 usage=1 dynamic=0
+; ANALYSIS-NEXT: 1: SV_SampleIndex rows=1 cols=1 at 4294967295:255 usage=0 
dynamic=0
+; ANALYSIS-NEXT: Outputs: 1 elements, 1 vectors
+
+; The DXIL signature element has a start row of -1 and start column of -1.
+; MD-DAG: !{i32 0, !"A", i8 9, i8 0, !{{[0-9]+}}, i8 2, i32 1, i8 1, i32 0, i8 
0, !{{[0-9]+}}}
+; MD-DAG: !{i32 1, !"SV_SampleIndex", i8 5, i8 12, !{{[0-9]+}}, i8 1, i32 1, 
i8 1, i32 -1, i8 -1, null}

>From d7989f414ed971547a48627e6a9f7035c877b39f Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 21:30:58 +0000
Subject: [PATCH 6/7] review: move the SampleIndex stage error to
 sample_index_error.ll

---
 llvm/test/CodeGen/DirectX/sample_index.ll     | 23 ++++---------------
 .../CodeGen/DirectX/sample_index_error.ll     | 11 +++++++++
 2 files changed, 16 insertions(+), 18 deletions(-)
 create mode 100644 llvm/test/CodeGen/DirectX/sample_index_error.ll

diff --git a/llvm/test/CodeGen/DirectX/sample_index.ll 
b/llvm/test/CodeGen/DirectX/sample_index.ll
index 0119d427ff3878..5265e00f17b372 100644
--- a/llvm/test/CodeGen/DirectX/sample_index.ll
+++ b/llvm/test/CodeGen/DirectX/sample_index.ll
@@ -1,28 +1,15 @@
-; RUN: split-file %s %t
-; RUN: opt -S -dxil-op-lower %t/pixel.ll | FileCheck %s
-; RUN: not opt -S -dxil-op-lower %t/vertex.ll 2>&1 | FileCheck %s 
--check-prefix=STAGE
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.0-pixel"
 
 ; CHECK-LABEL: define i32 @main(
 ; CHECK: %[[SI:.*]] = call i32 @dx.op.sampleIndex.i32(i32 90)
 ; CHECK: ret i32 %[[SI]]
-; CHECK: declare i32 @dx.op.sampleIndex.i32(i32) #[[#ATTR:]]
-; CHECK: attributes #[[#ATTR]] = { nounwind memory(none) }
-
-; SampleIndex is only available to pixel shaders.
-; STAGE: Cannot create SampleIndex operation: Invalid stage
-
-;--- pixel.ll
-target triple = "dxil-pc-shadermodel6.0-pixel"
 define i32 @main() {
 entry:
   %0 = call i32 @llvm.dx.sample.index()
   ret i32 %0
 }
 
-;--- vertex.ll
-target triple = "dxil-pc-shadermodel6.0-vertex"
-define i32 @main() {
-entry:
-  %0 = call i32 @llvm.dx.sample.index()
-  ret i32 %0
-}
+; CHECK: declare i32 @dx.op.sampleIndex.i32(i32) #[[#ATTR:]]
+; CHECK: attributes #[[#ATTR]] = { nounwind memory(none) }
diff --git a/llvm/test/CodeGen/DirectX/sample_index_error.ll 
b/llvm/test/CodeGen/DirectX/sample_index_error.ll
new file mode 100644
index 00000000000000..eb6707480a8467
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/sample_index_error.ll
@@ -0,0 +1,11 @@
+; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s
+
+; SampleIndex is only available to pixel shaders.
+; CHECK: Cannot create SampleIndex operation: Invalid stage
+
+target triple = "dxil-pc-shadermodel6.0-vertex"
+define i32 @main() {
+entry:
+  %0 = call i32 @llvm.dx.sample.index()
+  ret i32 %0
+}

>From 58487139716924f47fb2ef4383711b2028df161d Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 28 Oct 2026 21:30:58 +0000
Subject: [PATCH 7/7] review: document the integer type assumptions when
 loading SV_SampleIndex

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index fac0f7b850f71a..0438ddc5de448a 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1183,10 +1183,11 @@ static llvm::Value *createSPIRVInputLoad(
     IRBuilder<> &B, llvm::Module &M, llvm::Type *Ty, const Twine &Name,
     llvm::hlsl::SPIRVInterfaceDecoration Decoration, unsigned Operand,
     llvm::hlsl::InterpolationMode InterpMode) {
-  llvm::Type *LoadTy = Ty;
-  if (Decoration == llvm::hlsl::SPIRVInterfaceDecoration::BuiltIn &&
-      Ty->isIntegerTy() && Ty->getIntegerBitWidth() < 32)
-    LoadTy = B.getInt32Ty();
+  // SPIR-V integer built-ins are 32-bit, so a narrower integer declaration
+  // loads the 32-bit built-in and truncates it. Other types are loaded as is.
+  bool Truncate = Decoration == llvm::hlsl::SPIRVInterfaceDecoration::BuiltIn 
&&
+                  Ty->isIntegerTy() && Ty->getIntegerBitWidth() < 32;
+  llvm::Type *LoadTy = Truncate ? B.getInt32Ty() : Ty;
   auto *GV = new llvm::GlobalVariable(
       M, LoadTy, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
       /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
@@ -1195,7 +1196,7 @@ static llvm::Value *createSPIRVInputLoad(
   GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
   addSPIRVInterfaceDecorations(GV, Decoration, Operand, InterpMode);
   llvm::Value *Value = B.CreateLoad(LoadTy, GV);
-  return LoadTy == Ty ? Value : B.CreateTrunc(Value, Ty);
+  return Truncate ? B.CreateTrunc(Value, Ty) : Value;
 }
 
 llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad(
@@ -1713,6 +1714,9 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
                                   
llvm::hlsl::SPIRVInterfaceDecoration::BuiltIn,
                                   /* SampleId */ 18, InterpMode);
     if (CGM.getTarget().getTriple().isDXIL()) {
+      assert(Type->isIntegerTy() && Type->getIntegerBitWidth() <= 32 &&
+             "SV_SampleIndex must be a 16 or 32 bit scalar integer, as "
+             "diagnosed by Sema");
       // The sample index is read with a dedicated operation, but an unpacked
       // element is still added to the input signature for compatibility.
       SemanticShape Shape =

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to