[clang] [HLSL] cbuffer: Create host layout structs (PR #122820)
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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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