[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-24 Thread Helena Kotas via cfe-commits

https://github.com/hekota closed 
https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits

https://github.com/hekota edited 
https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits

https://github.com/hekota updated 
https://github.com/llvm/llvm-project/pull/122820

>From 71ddb5a2b4cc8a9609410b436e896484401f5e90 Mon Sep 17 00:00:00 2001
From: Helena Kotas 
Date: Mon, 13 Jan 2025 15:03:12 -0800
Subject: [PATCH 1/8] [HLSL] cbuffer: Create host layout struct and add
 resource handle to AST

Creates layout struct for `cbuffer` in Sema which will contains only 
declarations
contributing to the constant buffer layout. Anything else will be filtered out,
such as static variables decls, struct and function definitions, resources,
or empty struct and zero-sized arrays.

If the constant buffer includes a struct that contains any of the above 
undesirable
declarations, a new version of this struct should be created with these 
declarations
filtered out as well.

The definition of buffer layour struct is added to the HLSLBufferDecl node and 
is followed
by 'cbuffer` resource handle decl referencing the layout struct as its 
contained type.

Fixes #122553
---
 clang/include/clang/AST/Decl.h|   1 +
 clang/lib/AST/Decl.cpp|  13 +
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |   8 +-
 clang/lib/CodeGen/CGHLSLRuntime.h |   2 +-
 clang/lib/Sema/SemaHLSL.cpp   | 245 ++
 .../ast-dump-comment-cbuffe-tbufferr.hlsl |  15 +-
 clang/test/AST/HLSL/cbuffer.hlsl  | 217 
 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl  |  26 --
 clang/test/AST/HLSL/pch_hlsl_buffer.hlsl  |  17 +-
 9 files changed, 509 insertions(+), 35 deletions(-)
 create mode 100644 clang/test/AST/HLSL/cbuffer.hlsl
 delete mode 100644 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 9c470f09406378..0a66ed3d499ff2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4967,6 +4967,7 @@ class HLSLBufferDecl final : public NamedDecl, public 
DeclContext {
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
   void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
   bool isCBuffer() const { return IsCBuffer; }
+  const Type *getResourceHandleType() const;
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 31749e46458d6a..a4f5d1a3a71a63 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5693,6 +5693,19 @@ HLSLBufferDecl 
*HLSLBufferDecl::CreateDeserialized(ASTContext &C,
 SourceLocation(), SourceLocation());
 }
 
+const Type *HLSLBufferDecl::getResourceHandleType() const {
+  // Resource handle is the last decl in the HLSLBufferDecl.
+  // If it is not present, it probably means the buffer is empty.
+  if (VarDecl *VD = llvm::dyn_cast_or_null(LastDecl)) {
+const Type *Ty = VD->getType().getTypePtr();
+if (Ty->isHLSLAttributedResourceType()) {
+  assert(VD->getNameAsString() == "__handle");
+  return Ty;
+}
+  }
+  return nullptr;
+}
+
 
//===--===//
 // ImportDecl Implementation
 
//===--===//
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 5679bd71581795..51e20ad43fcc8d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -159,10 +159,12 @@ void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
   CB.Constants.emplace_back(std::make_pair(GV, LowerBound));
 }
 
-void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) {
-  for (Decl *it : DC->decls()) {
+void CGHLSLRuntime::addBufferDecls(const HLSLBufferDecl *D, Buffer &CB) {
+  for (Decl *it : D->decls()) {
 if (auto *ConstDecl = dyn_cast(it)) {
-  addConstant(ConstDecl, CB);
+  if (ConstDecl->getType().getTypePtr() != D->getResourceHandleType()) {
+addConstant(ConstDecl, CB);
+  }
 } else if (isa(it)) {
   // Nothing to do for this declaration.
 } else if (isa(it)) {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 00e110e8e6fa27..870a5bd0ea6b4f 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -169,7 +169,7 @@ class CGHLSLRuntime {
llvm::hlsl::ElementType ET,
BufferResBinding &Binding);
   void addConstant(VarDecl *D, Buffer &CB);
-  void addBufferDecls(const DeclContext *DC, Buffer &CB);
+  void addBufferDecls(const HLSLBufferDecl *D, Buffer &CB);
   llvm::Triple::ArchType getArch();
   llvm::SmallVector Buffers;
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 65ddee05a21512..c726672c0118e0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -21,10 +21,12 @@
 #include "clang/AST/TypeLo

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Tex Riddell via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Chris B via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;

llvm-beanz wrote:

`llvm::SmallString` might be a better option to reduce the likelihood of 
needing an allocation, and unifying `NameBase` and `Name` to a single 
`SmallString` would potentially reduce dynamic allocations and copying of 
string data.

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();

hekota wrote:

I see what you mean now. I have added a separate cbuffer AST test with 
namespaces and changed the implementation to always create the layout struct on 
the same context as the original struct so that it will be in the same 
namespace. The lookup is needs to scan just the immediate non-transparent decl 
context.

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))

hekota wrote:

There is an assert at the top of the `createHostLayoutStruct` function that 
will make sure the logic is consistent: 
```
assert(requiresImplicitBufferLayoutStructure(StructDecl) && "struct is already 
HLSL buffer compatible");
```

I have also moved the checking into a shared function 
`isInvalidConstantBufferLeafElementType`. Currently the only intangible types 
are resources classes, but I added a check for builtin resource type as well.



https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff ffe5cddb68ab84348866b3a3ac727d263b2a44c2 
80fc426305ace181d6ad44cf09e5896592b591c7 --extensions cpp -- 
clang/lib/Sema/SemaHLSL.cpp
``





View the diff from clang-format here.


``diff
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 1a8ae295e8..6edca32204 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -274,11 +274,10 @@ static bool isResourceRecordType(const Type *Ty) {
   return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
 }
 
-// Returns true if the type is a leaf element type that is not valid to be 
included
-// in HLSL Buffer, such as a resource class, empty struct, zero-sized array,
-// or a builtin intangible type.
-// Returns false it is a valid leaf element type or if it is a record type that
-// needs to be inspected further.
+// Returns true if the type is a leaf element type that is not valid to be
+// included in HLSL Buffer, such as a resource class, empty struct, zero-sized
+// array, or a builtin intangible type. Returns false it is a valid leaf 
element
+// type or if it is a record type that needs to be inspected further.
 static bool isInvalidConstantBufferLeafElementType(const Type *Ty) {
   if (Ty->isRecordType()) {
 if (isResourceRecordType(Ty) || Ty->getAsCXXRecordDecl()->isEmpty())

``




https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-23 Thread Helena Kotas via cfe-commits

https://github.com/hekota updated 
https://github.com/llvm/llvm-project/pull/122820

>From 71ddb5a2b4cc8a9609410b436e896484401f5e90 Mon Sep 17 00:00:00 2001
From: Helena Kotas 
Date: Mon, 13 Jan 2025 15:03:12 -0800
Subject: [PATCH 1/7] [HLSL] cbuffer: Create host layout struct and add
 resource handle to AST

Creates layout struct for `cbuffer` in Sema which will contains only 
declarations
contributing to the constant buffer layout. Anything else will be filtered out,
such as static variables decls, struct and function definitions, resources,
or empty struct and zero-sized arrays.

If the constant buffer includes a struct that contains any of the above 
undesirable
declarations, a new version of this struct should be created with these 
declarations
filtered out as well.

The definition of buffer layour struct is added to the HLSLBufferDecl node and 
is followed
by 'cbuffer` resource handle decl referencing the layout struct as its 
contained type.

Fixes #122553
---
 clang/include/clang/AST/Decl.h|   1 +
 clang/lib/AST/Decl.cpp|  13 +
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |   8 +-
 clang/lib/CodeGen/CGHLSLRuntime.h |   2 +-
 clang/lib/Sema/SemaHLSL.cpp   | 245 ++
 .../ast-dump-comment-cbuffe-tbufferr.hlsl |  15 +-
 clang/test/AST/HLSL/cbuffer.hlsl  | 217 
 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl  |  26 --
 clang/test/AST/HLSL/pch_hlsl_buffer.hlsl  |  17 +-
 9 files changed, 509 insertions(+), 35 deletions(-)
 create mode 100644 clang/test/AST/HLSL/cbuffer.hlsl
 delete mode 100644 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 9c470f09406378..0a66ed3d499ff2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4967,6 +4967,7 @@ class HLSLBufferDecl final : public NamedDecl, public 
DeclContext {
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
   void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
   bool isCBuffer() const { return IsCBuffer; }
+  const Type *getResourceHandleType() const;
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 31749e46458d6a..a4f5d1a3a71a63 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5693,6 +5693,19 @@ HLSLBufferDecl 
*HLSLBufferDecl::CreateDeserialized(ASTContext &C,
 SourceLocation(), SourceLocation());
 }
 
+const Type *HLSLBufferDecl::getResourceHandleType() const {
+  // Resource handle is the last decl in the HLSLBufferDecl.
+  // If it is not present, it probably means the buffer is empty.
+  if (VarDecl *VD = llvm::dyn_cast_or_null(LastDecl)) {
+const Type *Ty = VD->getType().getTypePtr();
+if (Ty->isHLSLAttributedResourceType()) {
+  assert(VD->getNameAsString() == "__handle");
+  return Ty;
+}
+  }
+  return nullptr;
+}
+
 
//===--===//
 // ImportDecl Implementation
 
//===--===//
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 5679bd71581795..51e20ad43fcc8d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -159,10 +159,12 @@ void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
   CB.Constants.emplace_back(std::make_pair(GV, LowerBound));
 }
 
-void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) {
-  for (Decl *it : DC->decls()) {
+void CGHLSLRuntime::addBufferDecls(const HLSLBufferDecl *D, Buffer &CB) {
+  for (Decl *it : D->decls()) {
 if (auto *ConstDecl = dyn_cast(it)) {
-  addConstant(ConstDecl, CB);
+  if (ConstDecl->getType().getTypePtr() != D->getResourceHandleType()) {
+addConstant(ConstDecl, CB);
+  }
 } else if (isa(it)) {
   // Nothing to do for this declaration.
 } else if (isa(it)) {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 00e110e8e6fa27..870a5bd0ea6b4f 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -169,7 +169,7 @@ class CGHLSLRuntime {
llvm::hlsl::ElementType ET,
BufferResBinding &Binding);
   void addConstant(VarDecl *D, Buffer &CB);
-  void addBufferDecls(const DeclContext *DC, Buffer &CB);
+  void addBufferDecls(const HLSLBufferDecl *D, Buffer &CB);
   llvm::Triple::ArchType getArch();
   llvm::SmallVector Buffers;
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 65ddee05a21512..c726672c0118e0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -21,10 +21,12 @@
 #include "clang/AST/TypeLo

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Chris B via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {
+if (isZeroSizedArray(cast(Ty)))
+  return nullptr;
+  }
+  QualType QT = QualType(Ty, 0);
+  ASTContext &AST = S.getASTContext();
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
+  auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
+  SourceLocation(), II, QT, TSI, nullptr, 
false,
+  InClassInitStyle::ICIS_NoInit);
+  Field->setAccess(AccessSpecifier::AS_private);
+  return Field;
+}
+
+// Creates host layout struct for a struct included in HLSL Buffer.
+// The layout struct will include only fields that are allowed in HLSL buffer.
+// Thes

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Tex Riddell via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))

tex3d wrote:

Won't we want to catch more intangible types than just resources here?  How do 
we make sure this is kept up-to-date for all intangible types?

On a related note, how do we ensure this code under `createHostLayoutStruct` 
always has matching/consistent logic to the code in 
`requiresImplicitBufferLayoutStructure`?  Could there be a way to share the key 
logic components perhaps?  For instance, there could be functions that return 
whether a leaf type is intangible (maybe this is just 
`Type::IsHLSLIntangibleType`), or is zero-sized, separate from the logic that 
recursively traverses a record type looking for these properties on field 
types.  The `IsHLSLIntangibleType` and zero-sized detection functions are then 
used by both traversal paths to ensure consistent behavior.

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https:/

[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();

hekota wrote:

This should only return zero or one result in the specified scope, which for 
the `HLSLBufferDecl` is the global scope. I will update the `LookupNameKind` to 
`LookupTagName` though to be more specific that the lookup is for a 
record/struct.

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Farzon Lotfi via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;

farzonl wrote:

Just wanted to make sure `StringRef ` was considered and this was intentional. 
no change needed. 

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Farzon Lotfi via cfe-commits

https://github.com/farzonl edited 
https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Farzon Lotfi via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,
+ HLSLBufferDecl *BufDecl);
+
+// 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,
+ IdentifierInfo *II,
+ CXXRecordDecl *LayoutStruct,
+ HLSLBufferDecl *BufDecl) {
+  if (Ty->isRecordType()) {
+if (isResourceRecordType(Ty))
+  return nullptr;
+CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+if (requiresImplicitBufferLayoutStructure(RD)) {
+  RD = createHostLayoutStruct(S, RD, BufDecl);
+  if (!RD)
+return nullptr;
+  Ty = RD->getTypeForDecl();
+}
+  } else if (Ty->isConstantArrayType()) {

farzonl wrote:

```suggestion
  }
  if (Ty->isConstantArrayType() && 
isZeroSizedArray(cast(Ty))) {
return nullptr;
}
```

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;

hekota wrote:

I can change `NameBase` to be `StringRef`, but in other places where this 
function it is creating new strings by appending the `StringRef` does not work, 
right? 

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Joshua Batista via cfe-commits

https://github.com/bob80905 approved this pull request.


https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Farzon Lotfi via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;
+  IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
+  if (!MustBeUnique)
+return II;
+
+  unsigned suffix = 0;
+  while (true) {
+if (suffix != 0)
+  II = &AST.Idents.get((llvm::Twine(Name) + "_" + Twine(suffix)).str(),
+   tok::TokenKind::identifier);
+if (!findRecordDecl(S, II, DC))
+  return II;
+// declaration with that name already exists - increment suffix and try
+// again until unique name is found
+suffix++;
+  };
+}
+
+// Returns true if the record type is an HLSL resource class
+static bool isResourceRecordType(const Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
+
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl 
*StructDecl,

farzonl wrote:

NIT: I usually like to put function decls at the top of the file before any 
function definitions to give me more freedom on code placement/movement. 

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Tex Riddell via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();

tex3d wrote:

Could this be multiple results if the name is defined in multiple accessible 
scopes (like current and parent scopes)?  Or does it only return zero or one 
result from the specified scope?

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Farzon Lotfi via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;

farzonl wrote:

many of the other functions in this file are using l`lvm::StringRef`. Why are 
we not doing the same thing for this function?

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Helena Kotas via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;

hekota wrote:

That's what I had originally, but having the name spellable enables additional 
layer of testing with using static asserts:

`_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, 
__layout_CB_1), "");`

The name starts with 2 underscores and that means it is reserved. Noone should 
be using it by accident.



https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-22 Thread Tex Riddell via cfe-commits


@@ -253,12 +257,229 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl 
*BufDecl) {
   }
 }
 
+// Returns true if the array has a zero size = if any of the dimensions is 0
+static bool isZeroSizedArray(const ConstantArrayType *CAT) {
+  while (CAT && !CAT->isZeroSize())
+CAT = dyn_cast(
+CAT->getElementType()->getUnqualifiedDesugaredType());
+  return CAT != nullptr;
+}
+
+// Returns true if the struct contains at least one element that prevents it
+// from being included inside HLSL Buffer as is, such as an intangible type,
+// empty struct, or zero-sized array. If it does, a new implicit layout struct
+// needs to be created for HLSL Buffer use that will exclude these unwanted
+// declarations (see createHostLayoutStruct function).
+static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) {
+  if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty())
+return true;
+  // check fields
+  for (const FieldDecl *Field : RD->fields()) {
+QualType Ty = Field->getType();
+if (Ty->isRecordType()) {
+  if (requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl()))
+return true;
+} else if (Ty->isConstantArrayType()) {
+  if (isZeroSizedArray(cast(Ty)))
+return true;
+}
+  }
+  // check bases
+  for (const CXXBaseSpecifier &Base : RD->bases())
+if (requiresImplicitBufferLayoutStructure(
+Base.getType()->getAsCXXRecordDecl()))
+  return true;
+  return false;
+}
+
+static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
+ DeclContext *DC) {
+  DeclarationNameInfo NameInfo =
+  DeclarationNameInfo(DeclarationName(II), SourceLocation());
+  LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getScopeForContext(DC));
+  if (R.isSingleResult())
+return R.getAsSingle();
+  return nullptr;
+}
+
+// Creates a name for buffer layout struct using the provide name base.
+// If the name must be unique (not previously defined), a suffix is added
+// until a unique name is found.
+static IdentifierInfo *getHostLayoutStructName(Sema &S,
+   IdentifierInfo *NameBaseII,
+   bool MustBeUnique,
+   DeclContext *DC) {
+  ASTContext &AST = S.getASTContext();
+  std::string NameBase;
+  if (NameBaseII) {
+NameBase = NameBaseII->getName().str();
+  } else {
+// anonymous struct
+NameBase = "anon";
+MustBeUnique = true;
+  }
+
+  std::string Name = "__layout_" + NameBase;

tex3d wrote:

Could we use a prefix/suffix that is unspellable in HLSL syntax?  Such as a 
`.host_layout` suffix or something?

https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-16 Thread Helena Kotas via cfe-commits

https://github.com/hekota edited 
https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)

2025-01-16 Thread Helena Kotas via cfe-commits

https://github.com/hekota edited 
https://github.com/llvm/llvm-project/pull/122820
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits