[clang] [HLSL] Fix SV_InstanceID to be zero-based on SPIR-V with non-zero FirstInstance (PR #225933)
Jonathan Zakharov via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 24 15:52:55 PDT 2026
https://github.com/jzakharovnv updated https://github.com/llvm/llvm-project/pull/225933
>From 53c1572127459472d8ac4bd89a53b58f3a1c39a7 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <jzakharov at nvidia.com>
Date: Wed, 23 Sep 2026 11:30:41 -0700
Subject: [PATCH 1/2] handle offset case
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 20 ++++++++++++++++---
.../semantics/SV_InstanceID.vs.hlsl | 12 ++++++++++-
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 52fde18dc3ebdb..624d8922c949a7 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1127,6 +1127,19 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
return B.CreateLoad(Ty, GV);
}
+// For Vulkan builtins which begin counting from a driver-supplied base offset,
+// where corresponding HLSL system value is always zero-based within current
+// draw call.
+static llvm::Value *createSPIRVBuiltinDelta(IRBuilder<> &B, llvm::Module &M,
+ llvm::Type *Ty, const Twine &Name,
+ unsigned BuiltInID,
+ unsigned BaseBuiltInID) {
+ llvm::Value *Value = createSPIRVBuiltinLoad(B, M, Ty, Name, BuiltInID);
+ llvm::Value *Base =
+ createSPIRVBuiltinLoad(B, M, Ty, Name + Twine(".base"), BaseBuiltInID);
+ return B.CreateSub(Value, Base);
+}
+
static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
llvm::Type *Ty, unsigned Location,
StringRef Name, bool NeedsFlat) {
@@ -1540,9 +1553,10 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
"SV_InstanceID 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(),
- /* BuiltIn::InstanceIndex */ 43);
+ return createSPIRVBuiltinDelta(B, CGM.getModule(), Type,
+ Semantic->getAttrName()->getName(),
+ /* BuiltIn::InstanceIndex */ 43,
+ /* BuiltIn::BaseInstance */ 4425);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
Signature);
diff --git a/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl b/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
index 363369356c6611..b7dd39047d1a73 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
@@ -2,10 +2,16 @@
// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SPIRV %s
// CHECK-SPIRV: @SV_InstanceID = external hidden thread_local addrspace(7) externally_initialized constant i32, !spirv.Decorations ![[#MD_0:]]
+// CHECK-SPIRV: @SV_InstanceID.base = external hidden thread_local addrspace(7) externally_initialized constant i32, !spirv.Decorations ![[#MD_BASE:]]
// CHECK: define void @main() {{.*}} {
uint main(uint id : SV_InstanceID) : A {
- // CHECK-SPIRV: %[[INPUT:.*]] = load i32, ptr addrspace(7) @SV_InstanceID, align 4
+ // Vulkan's InstanceIndex builtin is not zero-based when a non-zero
+ // FirstInstance is used, unlike HLSL's SV_InstanceID. Compensate by
+ // subtracting the BaseInstance builtin (matches DXC's SPIR-V backend).
+ // CHECK-SPIRV: %[[INDEX:.*]] = load i32, ptr addrspace(7) @SV_InstanceID, align 4
+ // CHECK-SPIRV: %[[BASE:.*]] = load i32, ptr addrspace(7) @SV_InstanceID.base, align 4
+ // CHECK-SPIRV: %[[INPUT:.*]] = sub i32 %[[INDEX]], %[[BASE]]
// CHECK-SPIRV: %[[RESULT:.*]] = call spir_func i32 @_Z4mainj(i32 %[[INPUT]])
// CHECK-DXIL: %[[INPUT:.*]] = call i32 @llvm.dx.load.input.i32(i32 0, i32 0, i8 0, i32 poison)
@@ -17,3 +23,7 @@ uint main(uint id : SV_InstanceID) : A {
// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 43}
// | `-> BuiltIn InstanceIndex
// `-> SPIR-V decoration 'BuiltIn'
+// CHECK-SPIRV-DAG: ![[#MD_BASE]] = !{![[#MD_BASE_1:]]}
+// CHECK-SPIRV-DAG: ![[#MD_BASE_1]] = !{i32 11, i32 4425}
+// | `-> BuiltIn BaseInstance
+// `-> SPIR-V decoration 'BuiltIn'
>From 92c03d25e5fb675c8d54109b4f20296ca7908f23 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <jzakharov at nvidia.com>
Date: Wed, 23 Sep 2026 14:08:23 -0700
Subject: [PATCH 2/2] comment clean up
---
clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl b/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
index b7dd39047d1a73..6b222c780feb23 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_InstanceID.vs.hlsl
@@ -7,8 +7,8 @@
// CHECK: define void @main() {{.*}} {
uint main(uint id : SV_InstanceID) : A {
// Vulkan's InstanceIndex builtin is not zero-based when a non-zero
- // FirstInstance is used, unlike HLSL's SV_InstanceID. Compensate by
- // subtracting the BaseInstance builtin (matches DXC's SPIR-V backend).
+ // FirstInstance is used, unlike HLSL's SV_InstanceID.
+ // Resolve by subtracting the BaseInstance builtin.
// CHECK-SPIRV: %[[INDEX:.*]] = load i32, ptr addrspace(7) @SV_InstanceID, align 4
// CHECK-SPIRV: %[[BASE:.*]] = load i32, ptr addrspace(7) @SV_InstanceID.base, align 4
// CHECK-SPIRV: %[[INPUT:.*]] = sub i32 %[[INDEX]], %[[BASE]]
More information about the cfe-commits
mailing list