=?utf-8?q?João?= Saffran <[email protected]>, =?utf-8?q?João?= Saffran <[email protected]> Message-ID: <llvm.org/llvm/llvm-project/pull/[email protected]> In-Reply-To:
https://github.com/joaosaffran created https://github.com/llvm/llvm-project/pull/186022 SPIRV backend uses 0 to represent unbounded arrays. This patch makes unbounded resources account for this, as well as makes sure the backend uses OpTypeRuntimeArray to represent such cases. >From d006275d84ee06058da410d0bc7236a41340a2e2 Mon Sep 17 00:00:00 2001 From: Joao Saffran <[email protected]> Date: Wed, 11 Mar 2026 14:17:16 -0700 Subject: [PATCH 1/3] 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; +; } + + + [email protected] = 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?= <[email protected]> Date: Wed, 11 Mar 2026 16:00:52 -0700 Subject: [PATCH 2/3] 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?= <[email protected]> Date: Wed, 11 Mar 2026 19:16:32 -0700 Subject: [PATCH 3/3] 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]] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
