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

Reply via email to