[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 23 20:27:16 PST 2025
================
@@ -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<ConstantArrayType>(
+ 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<ConstantArrayType>(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<CXXRecordDecl>();
+ 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<ConstantArrayType>(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.
+// These fields will be filtered out:
+// - resource classes
+// - empty structs
+// - zero-sized arrays
+// Returns nullptr if the resulting layout struct would be empty.
+static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl *StructDecl,
+ HLSLBufferDecl *BufDecl) {
+ assert(requiresImplicitBufferLayoutStructure(StructDecl) &&
+ "struct is already HLSL buffer compatible");
+
+ ASTContext &AST = S.getASTContext();
+ DeclContext *DC = StructDecl->getDeclContext();
+ IdentifierInfo *II = getHostLayoutStructName(
+ S, StructDecl->getIdentifier(), false, BufDecl->getDeclContext());
+
+ // reuse existing if the layout struct if it already exists
+ if (CXXRecordDecl *RD = findRecordDecl(S, II, DC))
+ return RD;
+
+ CXXRecordDecl *LS =
+ CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl,
+ SourceLocation(), SourceLocation(), II);
+ LS->setImplicit(true);
+ LS->startDefinition();
+
+ // copy base struct, create HLSL Buffer compatible version if needed
+ if (unsigned NumBases = StructDecl->getNumBases()) {
+ assert(NumBases == 1 && "HLSL supports only one base type");
----------------
hekota wrote:
It looks like we might not be supporting interfaces in Clang, so I'll remove the FIXME.
https://github.com/llvm/llvm-project/pull/122820
More information about the cfe-commits
mailing list