[clang] [llvm] [HLSL] Use 0 to represent unbounded resources (PR #186022)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 19:47:48 PDT 2026
=?utf-8?q?João?= Saffran <joaosaffranllvm at gmail.com>,
=?utf-8?q?João?= Saffran <joaosaffranllvm at gmail.com>,Joao Saffran
<joaosaffranllvm at gmail.com>,Joao Saffran <joaosaffranllvm at gmail.com>,Joao
Saffran <joaosaffranllvm at gmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186022 at github.com>
https://github.com/joaosaffran updated https://github.com/llvm/llvm-project/pull/186022
>From d006275d84ee06058da410d0bc7236a41340a2e2 Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Wed, 11 Mar 2026 14:17:16 -0700
Subject: [PATCH 1/6] replace -1 with 0 when targeting spirv
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 10 +++---
.../resources/res-array-global-unbounded.hlsl | 17 ++++++----
.../SPIRV/hlsl-resources/unbounded-arr.ll | 33 +++++++++++++++++++
3 files changed, 50 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index b695d016c0524..04daa5e611412 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -117,11 +117,13 @@ static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
}
// Get the total size of the array, or -1 if the array is unbounded.
-static int getTotalArraySize(ASTContext &AST, const clang::Type *Ty) {
+static int getTotalArraySize(ASTContext &AST, llvm::Triple::ArchType Arch,
+ const clang::Type *Ty) {
Ty = Ty->getUnqualifiedDesugaredType();
assert(Ty->isArrayType() && "expected array type");
if (Ty->isIncompleteArrayType())
- return -1;
+ // Spirv uses 0 to represent unbounded arrays.
+ return Arch == llvm::Triple::ArchType::dxil ? -1 : 0;
return AST.getConstantArrayElementCount(cast<ConstantArrayType>(Ty));
}
@@ -1280,7 +1282,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
// Calculate total array size (= range size).
llvm::Value *Range = llvm::ConstantInt::getSigned(
- CGM.IntTy, getTotalArraySize(AST, ResArrayTy));
+ CGM.IntTy, getTotalArraySize(AST, getArch(), ResArrayTy));
// If the result of the subscript operation is a single resource, call the
// constructor.
@@ -1345,7 +1347,7 @@ bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
AggValueSlot::DoesNotOverlap);
// Create Value for index and total array size (= range size).
- int Size = getTotalArraySize(AST, ResArrayTy);
+ int Size = getTotalArraySize(AST, getArch(), ResArrayTy);
llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0);
llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size);
diff --git a/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl b/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
index 6756a26bfc124..7d5b9626db138 100644
--- a/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
+++ b/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
@@ -30,14 +30,15 @@ void main(uint GI : SV_GroupIndex) {
// and explicit binding (u10, space1)
// CHECK: @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer.0") align {{(4|8)}} %[[Tmp0]],
- // CHECK-SAME: i32 noundef 10, i32 noundef 1, i32 noundef -1, i32 noundef 100, ptr noundef @A.str)
+ // DXIL-SAME: i32 noundef 10, i32 noundef 1, i32 noundef -1, i32 noundef 100, ptr noundef @A.str)
+ // SPV-SAME: i32 noundef 10, i32 noundef 1, i32 noundef 0, i32 noundef 100, ptr noundef @A.str)
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr{{.*}} @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} %[[Tmp0]], i32 noundef 0)
// CHECK-NEXT: %[[Value1:.*]] = load float, ptr{{.*}} %[[BufPtr]], align 4
// CHECK-NEXT: store float %[[Value1]], ptr %a, align 4
float a = A[100][0];
// Make sure B[2][3] is translated to a local RWBuffer<int>[4] array where each array element
- // is initialized by a constructor call with range -1 and index 52-55 and implicit binding
+ // is initialized by a constructor call with range 0 and index 52-55 and implicit binding
// (space 0, order_id 0)
// The first index is calculated from the array dimensions (unbounded x 5 x 4) and indices (2, 3)
// as 2 * 5 * 4 + 3 * 4 = 52 and the following indices are sequential.
@@ -45,22 +46,26 @@ void main(uint GI : SV_GroupIndex) {
// CHECK-NEXT: %[[Ptr_Tmp2_0:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 0
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_0]],
- // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 52, ptr noundef @[[BufB]])
+ // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 52, ptr noundef @[[BufB]])
+ // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 52, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_1:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 1
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_1]],
- // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 53, ptr noundef @[[BufB]])
+ // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 53, ptr noundef @[[BufB]])
+ // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 53, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_2:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 2
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_2]],
- // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 54, ptr noundef @[[BufB]])
+ // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 54, ptr noundef @[[BufB]])
+ // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 54, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_3:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 3
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_3]],
- // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 55, ptr noundef @[[BufB]])
+ // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 55, ptr noundef @[[BufB]])
+ // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 55, ptr noundef @[[BufB]])
// DXIL-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp1]], ptr align 4 %[[Tmp2]], i32 16, i1 false)
// SPV-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[Tmp1]], ptr align 8 %[[Tmp2]], i64 32, i1 false)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
new file mode 100644
index 0000000000000..4f1388a3605fc
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
@@ -0,0 +1,33 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[int32:[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: %[[rwbuffer:[0-9]+]] = OpTypeImage %[[int32]] Buffer 2 0 0 2 R32i
+; CHECK-DAG: OpTypeRuntimeArray %[[rwbuffer]]
+
+; This IR was emmited from the following HLSL code:
+; [[vk::binding(0)]]
+; RWBuffer<int> Buf[] : register(u0);
+;
+; [numthreads(4,2,1)]
+; void main(uint GI : SV_GroupIndex) {
+; Buf[0][0] = 0;
+; }
+
+
+
+ at Buf.str = private unnamed_addr constant [4 x i8] c"Buf\00", align 1
+
+; Function Attrs: convergent noinline norecurse
+define void @main() #0 {
+entry:
+ %2 = call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 24) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_24t(i32 0, i32 0, i32 0, i32 0, ptr @Buf.str)
+ %3 = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_24t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 24) %2, i32 0)
+ store i32 0, ptr addrspace(11) %3, align 4
+ ret void
+}
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 24) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_24t(i32, i32, i32, i32, ptr) #3
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="4,2,1" "hlsl.shader"="compute" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #3 = { nocallback nofree nosync nounwind willreturn memory(none) }
>From 201e771e6b272ada59047d17a9988465eff510c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Wed, 11 Mar 2026 16:00:52 -0700
Subject: [PATCH 2/6] add missing capability emission
---
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 3e50e4a0e8c80..5e966df6372db 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -6012,6 +6012,10 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
SC = GR.getPointerStorageClass(ResType);
}
+ if (ResType->getOpcode() == SPIRV::OpTypeImage && ArraySize == 0)
+ MIRBuilder.buildInstr(SPIRV::OpCapability)
+ .addImm(SPIRV::Capability::RuntimeDescriptorArrayEXT);
+
Register VarReg =
buildPointerToResource(SPIRVTypeInst(VarType), SC, Set, Binding,
ArraySize, IndexReg, Name, MIRBuilder);
>From ba10d096c7f6e4341697e67f131293a71de82bc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Wed, 11 Mar 2026 19:16:32 -0700
Subject: [PATCH 3/6] check if the capabily is being emited
---
llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
index 4f1388a3605fc..8be0da57a797b 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/unbounded-arr.ll
@@ -1,6 +1,7 @@
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %}
+; CHECK-DAG: OpCapability RuntimeDescriptorArrayEXT
; CHECK-DAG: %[[int32:[0-9]+]] = OpTypeInt 32 0
; CHECK-DAG: %[[rwbuffer:[0-9]+]] = OpTypeImage %[[int32]] Buffer 2 0 0 2 R32i
; CHECK-DAG: OpTypeRuntimeArray %[[rwbuffer]]
>From 1bc576f1bd7e95c9dc8fcae7b7cb5d2f16830c2c Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Thu, 12 Mar 2026 13:01:54 -0700
Subject: [PATCH 4/6] use 0 instead of -1
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 10 ++++------
.../resources/res-array-global-unbounded.hlsl | 15 +++++----------
llvm/docs/DirectX/DXILResources.rst | 2 +-
llvm/lib/Analysis/DXILResource.cpp | 4 +++-
.../CodeGen/DirectX/Binding/binding-overlap-6.ll | 2 +-
llvm/test/CodeGen/DirectX/CreateHandle.ll | 2 +-
.../CodeGen/DirectX/CreateHandleFromBinding.ll | 2 +-
.../test/CodeGen/DirectX/Metadata/srv_metadata.ll | 4 ++--
.../test/CodeGen/DirectX/Metadata/uav_metadata.ll | 4 ++--
.../DirectX/ResourceBindingAnalysisTests.cpp | 2 +-
10 files changed, 21 insertions(+), 26 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 04daa5e611412..c0f68e29e7e5d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -117,13 +117,11 @@ static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
}
// Get the total size of the array, or -1 if the array is unbounded.
-static int getTotalArraySize(ASTContext &AST, llvm::Triple::ArchType Arch,
- const clang::Type *Ty) {
+static int getTotalArraySize(ASTContext &AST, const clang::Type *Ty) {
Ty = Ty->getUnqualifiedDesugaredType();
assert(Ty->isArrayType() && "expected array type");
if (Ty->isIncompleteArrayType())
- // Spirv uses 0 to represent unbounded arrays.
- return Arch == llvm::Triple::ArchType::dxil ? -1 : 0;
+ return 0;
return AST.getConstantArrayElementCount(cast<ConstantArrayType>(Ty));
}
@@ -1282,7 +1280,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
// Calculate total array size (= range size).
llvm::Value *Range = llvm::ConstantInt::getSigned(
- CGM.IntTy, getTotalArraySize(AST, getArch(), ResArrayTy));
+ CGM.IntTy, getTotalArraySize(AST, ResArrayTy));
// If the result of the subscript operation is a single resource, call the
// constructor.
@@ -1347,7 +1345,7 @@ bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
AggValueSlot::DoesNotOverlap);
// Create Value for index and total array size (= range size).
- int Size = getTotalArraySize(AST, getArch(), ResArrayTy);
+ int Size = getTotalArraySize(AST, ResArrayTy);
llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0);
llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size);
diff --git a/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl b/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
index 7d5b9626db138..66bf71b3b0a35 100644
--- a/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
+++ b/clang/test/CodeGenHLSL/resources/res-array-global-unbounded.hlsl
@@ -30,8 +30,7 @@ void main(uint GI : SV_GroupIndex) {
// and explicit binding (u10, space1)
// CHECK: @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer.0") align {{(4|8)}} %[[Tmp0]],
- // DXIL-SAME: i32 noundef 10, i32 noundef 1, i32 noundef -1, i32 noundef 100, ptr noundef @A.str)
- // SPV-SAME: i32 noundef 10, i32 noundef 1, i32 noundef 0, i32 noundef 100, ptr noundef @A.str)
+ // CHECK-SAME: i32 noundef 10, i32 noundef 1, i32 noundef 0, i32 noundef 100, ptr noundef @A.str)
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr{{.*}} @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} %[[Tmp0]], i32 noundef 0)
// CHECK-NEXT: %[[Value1:.*]] = load float, ptr{{.*}} %[[BufPtr]], align 4
// CHECK-NEXT: store float %[[Value1]], ptr %a, align 4
@@ -46,26 +45,22 @@ void main(uint GI : SV_GroupIndex) {
// CHECK-NEXT: %[[Ptr_Tmp2_0:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 0
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_0]],
- // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 52, ptr noundef @[[BufB]])
- // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 52, ptr noundef @[[BufB]])
+ // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 52, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_1:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 1
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_1]],
- // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 53, ptr noundef @[[BufB]])
- // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 53, ptr noundef @[[BufB]])
+ // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 53, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_2:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 2
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_2]],
- // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 54, ptr noundef @[[BufB]])
- // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 54, ptr noundef @[[BufB]])
+ // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 54, ptr noundef @[[BufB]])
// CHECK-NEXT: %[[Ptr_Tmp2_3:.*]] = getelementptr [4 x %"class.hlsl::RWBuffer"], ptr %[[Tmp2]], i32 0, i32 3
// CHECK-NEXT: call void @hlsl::RWBuffer<int>::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*)
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align {{(4|8)}} %[[Ptr_Tmp2_3]],
- // DXIL-SAME: i32 noundef 0, i32 noundef 0, i32 noundef -1, i32 noundef 55, ptr noundef @[[BufB]])
- // SPV-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 55, ptr noundef @[[BufB]])
+ // CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 0, i32 noundef 55, ptr noundef @[[BufB]])
// DXIL-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp1]], ptr align 4 %[[Tmp2]], i32 16, i1 false)
// SPV-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[Tmp1]], ptr align 8 %[[Tmp2]], i64 32, i1 false)
diff --git a/llvm/docs/DirectX/DXILResources.rst b/llvm/docs/DirectX/DXILResources.rst
index d1841dfe0afcb..4471a248108cf 100644
--- a/llvm/docs/DirectX/DXILResources.rst
+++ b/llvm/docs/DirectX/DXILResources.rst
@@ -191,7 +191,7 @@ arguments.
* - ``%range_size``
- 3
- ``i32``
- - Range size of the binding, where ``UINT32_MAX (~0U)`` denotes an unbounded range.
+ - Range size of the binding, where ``0`` denotes an unbounded range.
* - ``%index``
- 4
- ``i32``
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index f01a6f80161a5..1a93de2f055dc 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -891,7 +891,7 @@ void DXILResourceMap::populateResourceInfos(Module &M,
ResourceInfo RI =
ResourceInfo{/*RecordID=*/0, Space, LowerBound,
- Size, HandleTy, Name};
+ Size == 0 ? ~0U : Size, HandleTy, Name};
CIToInfos.emplace_back(CI, RI, RTI);
}
@@ -1071,6 +1071,8 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
Value *Name = CI->getArgOperand(4);
+ if(Size == 0)
+ Size = ~0U;
// UINT32_MAX (~0U) size means unbounded resource array;
// upper bound register overflow should be detected in Sema
assert((Size == UINT32_MAX ||
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-6.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-6.ll
index a58e85b4159f1..dcca4feb31dae 100644
--- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-6.ll
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-6.ll
@@ -18,7 +18,7 @@ target triple = "dxil-pc-shadermodel6.3-library"
define void @test_overlapping() {
entry:
%h1 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, ptr @A.str)
- %h2 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 4, i32 -1, i32 0, ptr @B.str)
+ %h2 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 4, i32 0, i32 0, ptr @B.str)
%h3 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 17, i32 1, i32 0, ptr @C.str)
ret void
}
diff --git a/llvm/test/CodeGen/DirectX/CreateHandle.ll b/llvm/test/CodeGen/DirectX/CreateHandle.ll
index 6cca501bb2568..d92f4d369ad94 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandle.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandle.ll
@@ -61,7 +61,7 @@ define void @test_buffers() {
%typed3_ix = call i32 @some_val()
%typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
@llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0t(
- i32 0, i32 7, i32 -1, i32 %typed3_ix, ptr null)
+ i32 0, i32 7, i32 0, i32 %typed3_ix, ptr null)
; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %[[IX]], i1 false) #[[#ATTR]]
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
index 671fcef281314..561193508297b 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
@@ -66,7 +66,7 @@ define void @test_bindings() {
%typed3_ix = call i32 @some_val()
%typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
@llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0t(
- i32 0, i32 7, i32 -1, i32 %typed3_ix, ptr null)
+ i32 0, i32 7, i32 0, i32 %typed3_ix, ptr null)
; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
; CHECK: [[BUF5:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 -1, i32 0, i8 0 }, i32 %[[IX]], i1 false) #[[#ATTR]]
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF5]], %dx.types.ResourceProperties { i32 10, i32 1033 }) #[[#ATTR]]
diff --git a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll
index 0062f90326490..ea8f418d4dada 100644
--- a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll
+++ b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll
@@ -80,9 +80,9 @@ define void @test() #0 {
; Buffer<double> C1 = Array[10];
; Buffer<double> C2 = Array[20];
%Array2_10_h = call target("dx.TypedBuffer", double, 0, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 -1, i32 10, ptr @Array2.str)
+ @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 10, ptr @Array2.str)
%Array2_20_h = call target("dx.TypedBuffer", double, 0, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 -1, i32 20, ptr @Array2.str)
+ @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 20, ptr @Array2.str)
ret void
}
diff --git a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll
index d377a528abca1..0f7d56fb1261e 100644
--- a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll
+++ b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll
@@ -92,9 +92,9 @@ define void @test() #0 {
; RWBuffer<double> C1 = Array[10];
; RWBuffer<double> C2 = Array[20];
%Array2_10_h = call target("dx.TypedBuffer", double, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 -1, i32 10, ptr @Array2.str)
+ @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 10, ptr @Array2.str)
%Array2_20_h = call target("dx.TypedBuffer", double, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 -1, i32 20, ptr @Array2.str)
+ @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 20, ptr @Array2.str)
; Same buffer type as Nine - should have the same type in metadata
; RWBuffer<double> Ten : register(u2);
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index 3211c2c702aab..65e1beadf3e38 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -71,7 +71,7 @@ TEST_F(ResourceBindingAnalysisTest, TestOverlap) {
StringRef Assembly = R"(
define void @main() {
entry:
- %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, ptr null)
+ %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 0, i32 100, ptr null)
%handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, ptr null)
ret void
}
>From 1d7543ac2935bbb6357c75f55194d7f82a9e81f8 Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Thu, 12 Mar 2026 13:07:09 -0700
Subject: [PATCH 5/6] format
---
llvm/lib/Analysis/DXILResource.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 1a93de2f055dc..122288ef1e87f 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -889,9 +889,9 @@ void DXILResourceMap::populateResourceInfos(Module &M,
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
StringRef Name = getResourceNameFromBindingCall(CI);
- ResourceInfo RI =
- ResourceInfo{/*RecordID=*/0, Space, LowerBound,
- Size == 0 ? ~0U : Size, HandleTy, Name};
+ ResourceInfo RI = ResourceInfo{
+ /*RecordID=*/0, Space, LowerBound,
+ Size == 0 ? ~0U : Size, HandleTy, Name};
CIToInfos.emplace_back(CI, RI, RTI);
}
@@ -1071,7 +1071,7 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
Value *Name = CI->getArgOperand(4);
- if(Size == 0)
+ if (Size == 0)
Size = ~0U;
// UINT32_MAX (~0U) size means unbounded resource array;
// upper bound register overflow should be detected in Sema
>From d055cc681ae7e9be4aecb00e9924f7b9e66d0c9c Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Thu, 12 Mar 2026 19:47:33 -0700
Subject: [PATCH 6/6] save ongoing work
---
llvm/include/llvm/Analysis/DXILResource.h | 6 ++++--
llvm/lib/Analysis/DXILResource.cpp | 10 ++++------
llvm/lib/Target/DirectX/DXContainerGlobals.cpp | 4 ++--
llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll | 2 +-
.../test/CodeGen/DirectX/ContainerData/PSVResources.ll | 2 +-
llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll | 2 +-
6 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index f38de81d76a2c..6e345ea2fb611 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -384,8 +384,10 @@ class ResourceInfo {
return !(*this == RHS);
}
bool operator<(const ResourceBinding &RHS) const {
- return std::tie(RecordID, Space, LowerBound, Size) <
- std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHS.Size);
+ const uint32_t ThisSizeFixed = Size == 0 ? UINT32_MAX : Size;
+ const uint32_t RHSSizeFixed = Size == 0 ? UINT32_MAX : Size;
+ return std::tie(RecordID, Space, LowerBound, ThisSizeFixed) <
+ std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHSSizeFixed);
}
bool overlapsWith(const ResourceBinding &RHS) const {
if (Space != RHS.Space)
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 122288ef1e87f..5f342ba80481b 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -891,7 +891,7 @@ void DXILResourceMap::populateResourceInfos(Module &M,
ResourceInfo RI = ResourceInfo{
/*RecordID=*/0, Space, LowerBound,
- Size == 0 ? ~0U : Size, HandleTy, Name};
+ Size == 0 , HandleTy, Name};
CIToInfos.emplace_back(CI, RI, RTI);
}
@@ -1071,16 +1071,14 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
Value *Name = CI->getArgOperand(4);
- if (Size == 0)
- Size = ~0U;
- // UINT32_MAX (~0U) size means unbounded resource array;
+ // 0 size means unbounded resource array;
// upper bound register overflow should be detected in Sema
- assert((Size == UINT32_MAX ||
+ assert((Size == 0 ||
(uint64_t)LowerBound + (uint64_t)Size - 1ULL <=
(uint64_t)UINT32_MAX) &&
"upper bound register overflow");
uint32_t UpperBound =
- Size == UINT32_MAX ? UINT32_MAX : LowerBound + Size - 1;
+ Size == 0 ? UINT32_MAX : LowerBound + Size - 1;
Builder.trackBinding(RTI.getResourceClass(), Space, LowerBound,
UpperBound, Name);
}
diff --git a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
index 95577dd668e1e..5fdc498db57e1 100644
--- a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
+++ b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
@@ -194,10 +194,10 @@ void DXContainerGlobals::addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV) {
BindInfo.Type = Type;
BindInfo.LowerBound = Binding.LowerBound;
assert(
- (Binding.Size == UINT32_MAX ||
+ (Binding.Size == 0 ||
(uint64_t)Binding.LowerBound + Binding.Size - 1 <= UINT32_MAX) &&
"Resource range is too large");
- BindInfo.UpperBound = (Binding.Size == UINT32_MAX)
+ BindInfo.UpperBound = (Binding.Size == 0)
? UINT32_MAX
: Binding.LowerBound + Binding.Size - 1;
BindInfo.Space = Binding.Space;
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll
index 9c52d6ed3486a..151c770489826 100644
--- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll
@@ -29,7 +29,7 @@ entry:
; Buffer<double> C[] : register(t2, space4);
%h2 = call target("dx.TypedBuffer", double, 0, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 -1, i32 10, ptr @C.str)
+ @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 10, ptr @C.str)
ret void
}
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll
index d792078b8cbb7..ea3a02245c1cc 100644
--- a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll
@@ -105,7 +105,7 @@ define void @main() #0 {
; CHECK: UsedByAtomic64: false
; RWBuffer<float4> Buf = BufferArray[100];
%uav3 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 5, i32 10, i32 -1, i32 100, ptr null)
+ @llvm.dx.resource.handlefrombinding(i32 5, i32 10, i32 0, i32 100, ptr null)
ret void
}
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
index 561193508297b..482df78617c95 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
@@ -68,7 +68,7 @@ define void @test_bindings() {
@llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0t(
i32 0, i32 7, i32 0, i32 %typed3_ix, ptr null)
; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
- ; CHECK: [[BUF5:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 -1, i32 0, i8 0 }, i32 %[[IX]], i1 false) #[[#ATTR]]
+ ; CHECK: [[BUF5:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 0, i32 0, i8 0 }, i32 %[[IX]], i1 false) #[[#ATTR]]
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF5]], %dx.types.ResourceProperties { i32 10, i32 1033 }) #[[#ATTR]]
; cbuffer cb0 : register(b0) { int4 i; float4 f; }
More information about the cfe-commits
mailing list