https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/184276
>From a90196f4105de8c6168fc37ffdd80a37b5d285fe Mon Sep 17 00:00:00 2001 From: Helena Kotas <[email protected]> Date: Mon, 2 Mar 2026 17:06:02 -0800 Subject: [PATCH 1/2] [HLSL] Ignore complex types that do not contribute to cbuffer layout Fixes #183788 --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 6 --- clang/lib/Sema/SemaHLSL.cpp | 48 ++++++++++++++----- .../resources/cbuffer-empty-struct-array.hlsl | 41 ++++++++++++++++ 3 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 79d709867dc02..e518a7094077f 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -322,12 +322,6 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata( // Emit static and groupshared variables and resource classes inside // cbuffer as regular globals CGM.EmitGlobal(VD); - } else { - // Anything else that is not in the hlsl_constant address space must be - // an empty struct or a zero-sized array and can be ignored - assert(BufDecl->getASTContext().getTypeSize(VDTy) == 0 && - "constant buffer decl with non-zero sized type outside of " - "hlsl_constant address space"); } continue; } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 911dba40d3bde..a57cd622a4956 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -452,6 +452,31 @@ static IdentifierInfo *getHostLayoutStructName(Sema &S, NamedDecl *BaseDecl, }; } +static const Type *createHostLayoutType(Sema &S, const Type *Ty) { + ASTContext &AST = S.getASTContext(); + if (auto *RD = Ty->getAsCXXRecordDecl()) { + if (!requiresImplicitBufferLayoutStructure(RD)) + return Ty; + RD = createHostLayoutStruct(S, RD); + if (!RD) + return nullptr; + return AST.getCanonicalTagType(RD)->getTypePtr(); + } + + if (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) { + const Type *ElementTy = createHostLayoutType( + S, CAT->getElementType()->getUnqualifiedDesugaredType()); + if (!ElementTy) + return nullptr; + return AST + .getConstantArrayType(QualType(ElementTy, 0), CAT->getSize(), nullptr, + CAT->getSizeModifier(), + CAT->getIndexTypeCVRQualifiers()) + .getTypePtr(); + } + return Ty; +} + // Creates a field declaration of given name and type for HLSL buffer layout // struct. Returns nullptr if the type cannot be use in HLSL Buffer layout. static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty, @@ -460,14 +485,9 @@ static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty, if (isInvalidConstantBufferLeafElementType(Ty)) return nullptr; - if (auto *RD = Ty->getAsCXXRecordDecl()) { - if (requiresImplicitBufferLayoutStructure(RD)) { - RD = createHostLayoutStruct(S, RD); - if (!RD) - return nullptr; - Ty = S.Context.getCanonicalTagType(RD)->getTypePtr(); - } - } + Ty = createHostLayoutType(S, Ty); + if (!Ty) + return nullptr; QualType QT = QualType(Ty, 0); ASTContext &AST = S.getASTContext(); @@ -568,15 +588,21 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { VD->getType().getAddressSpace() == LangAS::hlsl_groupshared) continue; const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); + + QualType NewTy; if (FieldDecl *FD = createFieldForHostLayoutStruct(S, Ty, VD->getIdentifier(), LS)) { // add the field decl to the layout struct LS->addDecl(FD); // update address space of the original decl to hlsl_constant - QualType NewTy = - AST.getAddrSpaceQualType(VD->getType(), LangAS::hlsl_constant); - VD->setType(NewTy); + NewTy = AST.getAddrSpaceQualType(VD->getType(), LangAS::hlsl_constant); + } else { + // Declarations in default cbuffer $Globals had the address space set to + // hlsl_constant earlier. It needs to be removed now since it does not + // have a representable cbuffer layout. + NewTy = AST.removeAddrSpaceQualType(VD->getType()); } + VD->setType(NewTy); } LS->completeDefinition(); BufDecl->addLayoutStruct(LS); diff --git a/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl b/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl new file mode 100644 index 0000000000000..32ad278b1c42e --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes -o - -DCHECK2 %s | FileCheck %s -check-prefix=CHECK2 + +// Regression test for issue llvm/llvm-project#183788 + +// empty struct +struct A { +}; + +// struct with a resource which does not contribute to cbuffer layout +struct B { + RWBuffer<float> Buf; +}; + +cbuffer CB { + A a[2]; +#ifdef CHECK2 + int i; +#endif +} + +B b[2][2]; +#ifdef CHECK2 +int j; +#endif + +[numthreads(4,4,4)] +void main() { +} + +// CHECK-NOT: @CB.cb = global target("dx.CBuffer", %__cblayout_CB) +// CHECK-NOT: @A = external hidden addrspace(2) global +// CHECK-NOT: @B = external hidden addrspace(2) global +// CHECK-NOT: @"$Globals.cb" = global target("dx.CBuffer", + +// CHECK2: @CB.cb = global target("dx.CBuffer", %__cblayout_CB) +// CHECK2-NOT: @A = external hidden addrspace(2) global +// CHECK2: @i = external hidden addrspace(2) global i32 +// CHECK2: @"$Globals.cb" = global target("dx.CBuffer", +// CHECK2-NOT: @B = external hidden addrspace(2) global +// CHECK2: @j = external hidden addrspace(2) global i32 >From e03855b288b9bf2b88d92592cf3eb26e1bab36b1 Mon Sep 17 00:00:00 2001 From: Helena Kotas <[email protected]> Date: Tue, 10 Mar 2026 15:45:56 -0700 Subject: [PATCH 2/2] Check for empty decls in $Globals earlier; do not set hlsl_constant address space on these --- clang/lib/Sema/SemaHLSL.cpp | 67 +++++++++++++++---- clang/test/AST/HLSL/ast-dump-SpirvType.hlsl | 8 +-- clang/test/AST/HLSL/pch_spirv_type.hlsl | 2 +- .../resources/cbuffer-empty-struct-array.hlsl | 32 +++++++-- 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index a57cd622a4956..1717756c7cbab 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -571,7 +571,7 @@ static CXXRecordDecl *createHostLayoutStruct(Sema &S, // - zero-sized arrays // - non-variable declarations // The layout struct will be added to the HLSLBufferDecl declarations. -void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { +static void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { ASTContext &AST = S.getASTContext(); IdentifierInfo *II = getHostLayoutStructName(S, BufDecl, true); @@ -589,20 +589,27 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { continue; const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); - QualType NewTy; - if (FieldDecl *FD = - createFieldForHostLayoutStruct(S, Ty, VD->getIdentifier(), LS)) { - // add the field decl to the layout struct + FieldDecl *FD = + createFieldForHostLayoutStruct(S, Ty, VD->getIdentifier(), LS); + // Declarations collected for the default $Globals constant buffer have + // already been checked to have non-empty cbuffer layout, so + // createFieldForHostLayoutStruct should always succeed. These declarations + // already have their address space set to hlsl_constant. + // For declarations in a named cbuffer block + // createFieldForHostLayoutStruct can still return nullptr if the type + // is empty (does not have a cbuffer layout). + assert((FD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) && + "host layout field for $Globals decl failed to be created"); + if (FD) { + // Add the field decl to the layout struct. LS->addDecl(FD); - // update address space of the original decl to hlsl_constant - NewTy = AST.getAddrSpaceQualType(VD->getType(), LangAS::hlsl_constant); - } else { - // Declarations in default cbuffer $Globals had the address space set to - // hlsl_constant earlier. It needs to be removed now since it does not - // have a representable cbuffer layout. - NewTy = AST.removeAddrSpaceQualType(VD->getType()); + if (VD->getType().getAddressSpace() != LangAS::hlsl_constant) { + // Update address space of the original decl to hlsl_constant. + QualType NewTy = + AST.getAddrSpaceQualType(VD->getType(), LangAS::hlsl_constant); + VD->setType(NewTy); + } } - VD->setType(NewTy); } LS->completeDefinition(); BufDecl->addLayoutStruct(LS); @@ -4405,6 +4412,38 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) { return Ty; } +// Returns true if the type has a non-empty constant buffer layout (if it is +// scalar, vector or matrix, or if it contains any of these. +static bool hasConstantBufferLayout(QualType QT) { + const Type *Ty = QT->getUnqualifiedDesugaredType(); + if (Ty->isScalarType() || Ty->isVectorType() || Ty->isMatrixType()) + return true; + + if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray()) + return false; + + if (const auto *RD = Ty->getAsCXXRecordDecl()) { + for (const auto *FD : RD->fields()) { + if (hasConstantBufferLayout(FD->getType())) + return true; + } + assert(RD->getNumBases() <= 1 && + "HLSL doesn't support multiple inheritance"); + return RD->getNumBases() + ? hasConstantBufferLayout(RD->bases_begin()->getType()) + : false; + } + + if (const auto *AT = dyn_cast<ArrayType>(Ty)) { + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) + if (isZeroSizedArray(CAT)) + return false; + return hasConstantBufferLayout(AT->getElementType()); + } + + return false; +} + static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) { bool IsVulkan = Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan; @@ -4414,7 +4453,7 @@ static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) { QT.getAddressSpace() == LangAS::Default && VD->getStorageClass() != SC_Static && !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant && - !isInvalidConstantBufferLeafElementType(QT.getTypePtr()); + hasConstantBufferLayout(QT); } void SemaHLSL::deduceAddressSpace(VarDecl *Decl) { diff --git a/clang/test/AST/HLSL/ast-dump-SpirvType.hlsl b/clang/test/AST/HLSL/ast-dump-SpirvType.hlsl index a4a147622c6cc..e6ea31341a3cc 100644 --- a/clang/test/AST/HLSL/ast-dump-SpirvType.hlsl +++ b/clang/test/AST/HLSL/ast-dump-SpirvType.hlsl @@ -5,19 +5,19 @@ typedef vk::SpirvOpaqueType<123, RWBuffer<float>, vk::integral_constant<uint, 4> // CHECK: TypedefDecl 0x{{.+}} <{{.+}}:6:1, col:133> col:133 referenced BType 'vk::SpirvType<12, 2, 4, vk::integral_constant<uint64_t, 4886718345L>, float, vk::Literal<vk::integral_constant<uint, 456>>>':'__hlsl_spirv_type<12, 2, 4, vk::integral_constant<unsigned long, 4886718345>, float, vk::Literal<vk::integral_constant<uint, 456>>>' typedef vk::SpirvType<12, 2, 4, vk::integral_constant<uint64_t, 0x123456789>, float, vk::Literal<vk::integral_constant<uint, 456>>> BType; -// CHECK: VarDecl 0x{{.+}} <{{.+}}:9:1, col:7> col:7 AValue 'hlsl_constant AType':'hlsl_constant __hlsl_spirv_type<123, 0, 0, RWBuffer<float>, vk::integral_constant<unsigned int, 4>>' +// CHECK: VarDecl 0x{{.+}} <{{.+}}:9:1, col:7> col:7 AValue 'AType':'__hlsl_spirv_type<123, 0, 0, RWBuffer<float>, vk::integral_constant<unsigned int, 4>>' AType AValue; -// CHECK: VarDecl 0x{{.+}} <{{.+}}:11:1, col:7> col:7 BValue 'hlsl_constant BType':'hlsl_constant __hlsl_spirv_type<12, 2, 4, vk::integral_constant<unsigned long, 4886718345>, float, vk::Literal<vk::integral_constant<uint, 456>>>' +// CHECK: VarDecl 0x{{.+}} <{{.+}}:11:1, col:7> col:7 BValue 'BType':'__hlsl_spirv_type<12, 2, 4, vk::integral_constant<unsigned long, 4886718345>, float, vk::Literal<vk::integral_constant<uint, 456>>>' BType BValue; -// CHECK: VarDecl 0x{{.+}} <{{.+}}:14:1, col:80> col:80 CValue 'hlsl_constant vk::SpirvOpaqueType<123, vk::Literal<vk::integral_constant<uint, 305419896>>>':'hlsl_constant __hlsl_spirv_type<123, 0, 0, vk::Literal<vk::integral_constant<uint, 305419896>>>' +// CHECK: VarDecl 0x{{.+}} <{{.+}}:14:1, col:80> col:80 CValue 'vk::SpirvOpaqueType<123, vk::Literal<vk::integral_constant<uint, 305419896>>>':'__hlsl_spirv_type<123, 0, 0, vk::Literal<vk::integral_constant<uint, 305419896>>>' vk::SpirvOpaqueType<123, vk::Literal<vk::integral_constant<uint, 0x12345678>>> CValue; // CHECK: TypeAliasDecl 0x{{.+}} <{{.+}}:18:1, col:72> col:7 Array 'vk::SpirvOpaqueType<28, T, vk::integral_constant<uint, L>>':'__hlsl_spirv_type<28U, 0, 0, T, vk::integral_constant<uint, L>>' template <class T, uint L> using Array = vk::SpirvOpaqueType<28, T, vk::integral_constant<uint, L>>; -// CHECK: VarDecl 0x{{.+}} <{{.+}}:21:1, col:16> col:16 DValue 'hlsl_constant Array<uint, 5>':'hlsl_constant __hlsl_spirv_type<28, 0, 0, uint, vk::integral_constant<unsigned int, 5>>' +// CHECK: VarDecl 0x{{.+}} <{{.+}}:21:1, col:16> col:16 DValue 'Array<uint, 5>':'__hlsl_spirv_type<28, 0, 0, uint, vk::integral_constant<unsigned int, 5>>' Array<uint, 5> DValue; [numthreads(1, 1, 1)] diff --git a/clang/test/AST/HLSL/pch_spirv_type.hlsl b/clang/test/AST/HLSL/pch_spirv_type.hlsl index 045f89a1b8461..5b34956d0db1c 100644 --- a/clang/test/AST/HLSL/pch_spirv_type.hlsl +++ b/clang/test/AST/HLSL/pch_spirv_type.hlsl @@ -6,7 +6,7 @@ // Make sure PCH works by using function declared in PCH header and declare a SpirvType in current file. // CHECK:FunctionDecl 0x[[FOO:[0-9a-f]+]] <{{.*}}:2:1, line:4:1> line:2:8 imported used foo 'float2 (float2, float2)' -// CHECK:VarDecl 0x{{[0-9a-f]+}} <{{.*}}:10:1, col:92> col:92 buffers2 'hlsl_constant vk::SpirvOpaqueType<28, RWBuffer<float>, vk::integral_constant<uint, 4>>':'hlsl_constant __hlsl_spirv_type<28, 0, 0, RWBuffer<float>, vk::integral_constant<unsigned int, 4>>' +// CHECK:VarDecl 0x{{[0-9a-f]+}} <{{.*}}:10:1, col:92> col:92 buffers2 'vk::SpirvOpaqueType<28, RWBuffer<float>, vk::integral_constant<uint, 4>>':'__hlsl_spirv_type<28, 0, 0, RWBuffer<float>, vk::integral_constant<unsigned int, 4>>' vk::SpirvOpaqueType</* OpTypeArray */ 28, RWBuffer<float>, vk::integral_constant<uint, 4>> buffers2; float2 bar(float2 a, float2 b) { diff --git a/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl b/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl index 32ad278b1c42e..2e92d071202ae 100644 --- a/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl +++ b/clang/test/CodeGenHLSL/resources/cbuffer-empty-struct-array.hlsl @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes -o - -DCHECK2 %s | FileCheck %s -check-prefix=CHECK2 +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes \ +// RUN: -finclude-default-header -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -emit-llvm -disable-llvm-passes \ +// RUN: -finclude-default-header -o - -DCHECK2 %s | FileCheck %s -check-prefix=CHECK2 // Regression test for issue llvm/llvm-project#183788 @@ -12,16 +14,28 @@ struct B { RWBuffer<float> Buf; }; +struct C : A { + B b; + RWBuffer<float> Bufs[10]; + A array[10][2]; +}; + cbuffer CB { A a[2]; + C c; #ifdef CHECK2 int i; + float2 v; + int4x4 m; #endif } B b[2][2]; + #ifdef CHECK2 int j; +float2 w; +int4x4 n; #endif [numthreads(4,4,4)] @@ -29,13 +43,19 @@ void main() { } // CHECK-NOT: @CB.cb = global target("dx.CBuffer", %__cblayout_CB) -// CHECK-NOT: @A = external hidden addrspace(2) global -// CHECK-NOT: @B = external hidden addrspace(2) global +// CHECK-NOT: @a = external hidden addrspace(2) global +// CHECK-NOT: @b = external hidden addrspace(2) global +// CHECK-NOT: @c = external hidden addrspace(2) global // CHECK-NOT: @"$Globals.cb" = global target("dx.CBuffer", // CHECK2: @CB.cb = global target("dx.CBuffer", %__cblayout_CB) -// CHECK2-NOT: @A = external hidden addrspace(2) global +// CHECK-NOT: @a = external hidden addrspace(2) global +// CHECK-NOT: @c = external hidden addrspace(2) global // CHECK2: @i = external hidden addrspace(2) global i32 +// CHECK2: @v = external hidden addrspace(2) global <2 x float>, align 8 +// CHECK2: @m = external hidden addrspace(2) global [4 x <4 x i32>], align 4 // CHECK2: @"$Globals.cb" = global target("dx.CBuffer", -// CHECK2-NOT: @B = external hidden addrspace(2) global +// CHECK-NOT: @b = external hidden addrspace(2) global // CHECK2: @j = external hidden addrspace(2) global i32 +// CHECK2: @w = external hidden addrspace(2) global <2 x float>, align 8 +// CHECK2: @n = external hidden addrspace(2) global [4 x <4 x i32>], align 4 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
