diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 5001883003ee2..f26469e6a2f1d 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -15,12 +15,14 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclarationName.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" @@ -32,16 +34,21 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DXILABI.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/TargetParser/Triple.h" +#include #include #include using namespace clang; using RegisterType = HLSLResourceBindingAttr::RegisterType; +static CXXRecordDecl *createHostLayoutStruct(Sema &S, + CXXRecordDecl *StructDecl); + static RegisterType getRegisterType(ResourceClass RC) { switch (RC) { case ResourceClass::SRV: @@ -253,12 +260,244 @@ 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 record type is an HLSL resource class +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. +static bool isInvalidConstantBufferLeafElementType(const Type *Ty) { + if (Ty->isRecordType()) { + if (isResourceRecordType(Ty) || Ty->getAsCXXRecordDecl()->isEmpty()) + return true; + return false; + } + if (Ty->isConstantArrayType() && + isZeroSizedArray(cast(Ty))) + return true; + if (Ty->isHLSLBuiltinIntangibleType()) + return true; + return false; +} + +// 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 (isInvalidConstantBufferLeafElementType(Ty.getTypePtr())) + return true; + if (Ty->isRecordType() && + requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl())) + return true; + } + // check bases + for (const CXXBaseSpecifier &Base : RD->bases()) + if (requiresImplicitBufferLayoutStructure( + Base.getType()->getAsCXXRecordDecl())) + return true; + return false; +} + +static CXXRecordDecl *findRecordDeclInContext(IdentifierInfo *II, + DeclContext *DC) { + CXXRecordDecl *RD = nullptr; + for (NamedDecl *Decl : + DC->getNonTransparentContext()->lookup(DeclarationName(II))) { + if (CXXRecordDecl *FoundRD = dyn_cast(Decl)) { + assert(RD == nullptr && + "there should be at most 1 record by a given name in a scope"); + RD = FoundRD; + } + } + return RD; +} + +// 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, NamedDecl *BaseDecl, + bool MustBeUnique) { + ASTContext &AST = S.getASTContext(); + + IdentifierInfo *NameBaseII = BaseDecl->getIdentifier(); + llvm::SmallString<64> Name("__layout_"); + if (NameBaseII) { + Name.append(NameBaseII->getName()); + } else { + // anonymous struct + Name.append("anon"); + MustBeUnique = true; + } + + size_t NameLength = Name.size(); + IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier); + if (!MustBeUnique) + return II; + + unsigned suffix = 0; + while (true) { + if (suffix != 0) { + Name.append("_"); + Name.append(llvm::Twine(suffix).str()); + II = &AST.Idents.get(Name, tok::TokenKind::identifier); + } + if (!findRecordDeclInContext(II, BaseDecl->getDeclContext())) + return II; + // declaration with that name already exists - increment suffix and try + // again until unique name is found + suffix++; + Name.truncate(NameLength); + }; +} + +// 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) { + if (isInvalidConstantBufferLeafElementType(Ty)) + return nullptr; + + if (Ty->isRecordType()) { + CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + if (requiresImplicitBufferLayoutStructure(RD)) { + RD = createHostLayoutStruct(S, RD); + if (!RD) + return nullptr; + Ty = RD->getTypeForDecl(); + } + } + + 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) { + assert(requiresImplicitBufferLayoutStructure(StructDecl) && + "struct is already HLSL buffer compatible"); + + ASTContext &AST = S.getASTContext(); + DeclContext *DC = StructDecl->getDeclContext(); + IdentifierInfo *II = getHostLayoutStructName(S, StructDecl, false); + + // reuse existing if the layout struct if it already exists + if (CXXRecordDecl *RD = findRecordDeclInContext(II, DC)) + return RD; + + CXXRecordDecl *LS = CXXRecordDecl::Create( + AST, TagDecl::TagKind::Class, DC, 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"); + CXXBaseSpecifier Base = *StructDecl->bases_begin(); + CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); + if (requiresImplicitBufferLayoutStructure(BaseDecl)) { + BaseDecl = createHostLayoutStruct(S, BaseDecl); + if (BaseDecl) { + TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo( + QualType(BaseDecl->getTypeForDecl(), 0)); + Base = CXXBaseSpecifier(SourceRange(), false, StructDecl->isClass(), + AS_none, TSI, SourceLocation()); + } + } + if (BaseDecl) { + const CXXBaseSpecifier *BasesArray[1] = {&Base}; + LS->setBases(BasesArray, 1); + } + } + + // filter struct fields + for (const FieldDecl *FD : StructDecl->fields()) { + const Type *Ty = FD->getType()->getUnqualifiedDesugaredType(); + if (FieldDecl *NewFD = + createFieldForHostLayoutStruct(S, Ty, FD->getIdentifier(), LS)) + LS->addDecl(NewFD); + } + LS->completeDefinition(); + + if (LS->field_empty() && LS->getNumBases() == 0) + return nullptr; + + DC->addDecl(LS); + return LS; +} + +// Creates host layout struct for HLSL Buffer. The struct will include only +// fields of types that are allowed in HLSL buffer and it will filter out: +// - static variable declarations +// - resource classes +// - empty structs +// - zero-sized arrays +// - non-variable declarations +// The layour struct will be added to the HLSLBufferDecl declarations. +void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { + ASTContext &AST = S.getASTContext(); + IdentifierInfo *II = getHostLayoutStructName(S, BufDecl, true); + + CXXRecordDecl *LS = + CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl, + SourceLocation(), SourceLocation(), II); + LS->setImplicit(true); + LS->startDefinition(); + + for (const Decl *D : BufDecl->decls()) { + const VarDecl *VD = dyn_cast(D); + if (!VD || VD->getStorageClass() == SC_Static) + continue; + const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); + if (FieldDecl *FD = + createFieldForHostLayoutStruct(S, Ty, VD->getIdentifier(), LS)) + LS->addDecl(FD); + } + LS->completeDefinition(); + BufDecl->addDecl(LS); +} + +// Handle end of cbuffer/tbuffer declaration void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) { auto *BufDecl = cast(Dcl); BufDecl->setRBraceLoc(RBrace); validatePackoffset(SemaRef, BufDecl); + // create buffer layout struct + createHostLayoutStructForBuffer(SemaRef, BufDecl); + SemaRef.PopDeclContext(); } diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffer-tbuffer.hlsl similarity index 50% rename from clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl rename to clang/test/AST/HLSL/ast-dump-comment-cbuffer-tbuffer.hlsl index e6a2ea7c6d2dc..0bff3ae144037 100644 --- a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl +++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffer-tbuffer.hlsl @@ -37,19 +37,26 @@ tbuffer B { int d; } -// AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A -// AST-NEXT:-HLSLResourceClassAttr {{.*}} <> Implicit CBuffer -// AST-NEXT:-HLSLResourceAttr {{.*}} <> Implicit CBuffer -// AST-NEXT:FullComment {{.*}} -// AST-NEXT:`-ParagraphComment {{.*}} -// AST-NEXT:`-TextComment {{.*}} Text=" CBuffer decl." -// AST-NEXT:-VarDecl {{.*}} col:11 a 'float' -// AST-NEXT:`-VarDecl {{.*}} col:9 b 'int' -// AST-NEXT:HLSLBufferDecl {{.*}} line:29:9 tbuffer B -// AST-NEXT:-HLSLResourceClassAttr {{.*}} <> Implicit SRV -// AST-NEXT:-HLSLResourceAttr {{.*}} <> Implicit TBuffer -// AST-NEXT:-FullComment {{.*}} -// AST-NEXT: `-ParagraphComment {{.*}} -// AST-NEXT: `-TextComment {{.*}} Text=" TBuffer decl." -// AST-NEXT:-VarDecl {{.*}} col:11 c 'float' -// AST-NEXT:`-VarDecl {{.*}} col:9 d 'int' +// AST: HLSLBufferDecl {{.*}} line:11:9 cbuffer A +// AST-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// AST-NEXT: HLSLResourceAttr {{.*}} Implicit CBuffer +// AST-NEXT: FullComment +// AST-NEXT: ParagraphComment +// AST-NEXT: TextComment {{.*}} Text=" CBuffer decl." +// AST-NEXT: VarDecl {{.*}} a 'float' +// AST-NEXT: VarDecl {{.*}} b 'int' +// AST-NEXT: CXXRecordDecl {{.*}} implicit class __layout_A definition +// AST: FieldDecl {{.*}} a 'float' +// AST-NEXT: FieldDecl {{.*}} b 'int' + +// AST-NEXT: HLSLBufferDecl {{.*}} line:29:9 tbuffer B +// AST-NEXT: HLSLResourceClassAttr {{.*}} Implicit SRV +// AST-NEXT: HLSLResourceAttr {{.*}} Implicit TBuffer +// AST-NEXT: FullComment +// AST-NEXT: ParagraphComment +// AST-NEXT: TextComment {{.*}} Text=" TBuffer decl." +// AST-NEXT: VarDecl {{.*}} c 'float' +// AST-NEXT: VarDecl {{.*}} d 'int' +// AST-NEXT: CXXRecordDecl {{.*}} implicit class __layout_B definition +// AST: FieldDecl {{.*}} c 'float' +// AST-NEXT: FieldDecl {{.*}} d 'int' diff --git a/clang/test/AST/HLSL/cbuffer.hlsl b/clang/test/AST/HLSL/cbuffer.hlsl new file mode 100644 index 0000000000000..721abb290f163 --- /dev/null +++ b/clang/test/AST/HLSL/cbuffer.hlsl @@ -0,0 +1,209 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s + +struct EmptyStruct { +}; + +struct A { + float a; +}; + +struct B { + RWBuffer buf; + EmptyStruct es; + float ea[0]; + float a; +}; + +struct C { + EmptyStruct es; +}; + +typedef B BTypedef; +typedef C CTypedef; + +struct D : B { + float b; +}; + +struct E : EmptyStruct { + float c; +}; + +struct F : A { + int ae[0]; +}; + +typedef float EmptyArrayTypedef[10][0]; + +struct OneFloat { + float a; +}; + +struct TwoFloats { + float a; + float b; +}; + +// CHECK: HLSLBufferDecl {{.*}} line:50:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} col:9 used a1 'float' + float a1; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB definition + // CHECK: FieldDecl {{.*}} a1 'float' +} +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_CB), ""); + +// Check that buffer layout struct does not include resources or empty types +// CHECK: HLSLBufferDecl {{.*}} line:62:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} col:9 used a2 'float' + float a2; + // CHECK: VarDecl {{.*}} col:19 b2 'RWBuffer':'hlsl::RWBuffer' + RWBuffer b2; + // CHECK: VarDecl {{.*}} col:15 c2 'EmptyStruct' + EmptyStruct c2; + // CHECK: VarDecl {{.*}} col:9 d2 'float[0]' + float d2[0]; + // CHECK: VarDecl {{.*}} col:9 e2 'float' + float e2; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_1 definition + // CHECK: FieldDecl {{.*}} a2 'float' + // CHECK-NEXT: FieldDecl {{.*}} e2 'float' +} +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_1), ""); + +// Check that layout struct is created for B and the empty struct C is removed +// CHECK: HLSLBufferDecl {{.*}} line:83:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} col:5 used s1 'A' + A s1; + // CHECK: VarDecl {{.*}} col:5 s2 'B' + B s2; + // CHECK: VarDecl {{.*}} col:12 s3 'CTypedef':'C + CTypedef s3; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_2 definition + // CHECK: FieldDecl {{.*}} s1 'A' + // CHECK: FieldDecl {{.*}} s2 '__layout_B' +} +// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_B definition +// CHECK: FieldDecl {{.*}} a 'float' + +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_B), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_2), ""); + +// check that layout struct is created for D because of its base struct +// CHECK: HLSLBufferDecl {{.*}} line:104:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} s4 'D' + D s4; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_3 definition + // CHECK: FieldDecl {{.*}} s4 '__layout_D' +} + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_D definition + // CHECK: public '__layout_B' + // CHECK: FieldDecl {{.*}} b 'float' +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_D), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_3), ""); + +// check that layout struct is created for E because because its base struct +// is empty and should be eliminated, and BTypedef should reuse the previously +// defined '__layout_B' +// CHECK: HLSLBufferDecl {{.*}} line:122:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} s5 'E' + E s5; + // CHECK: VarDecl {{.*}} s6 'BTypedef':'B' + BTypedef s6; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_4 definition + // CHECK: FieldDecl {{.*}} s5 '__layout_E' + // CHECK: FieldDecl {{.*}} s6 '__layout_B' +} + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_E definition + // CHECK: FieldDecl {{.*}} c 'float' + // CHECK-NOT: CXXRecordDecl {{.*}} class __layout_B definition +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_E), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_4), ""); + +// check that this produces empty layout struct +// CHECK: HLSLBufferDecl {{.*}} line:141:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: FunctionDecl {{.*}} f 'void ()' + void f() {} + // CHECK: VarDecl {{.*}} SV 'float' static + static float SV; + // CHECK: VarDecl {{.*}} s7 'EmptyStruct' callinit + EmptyStruct s7; + // CHECK: VarDecl {{.*}} Buf 'RWBuffer':'hlsl::RWBuffer' callinit + RWBuffer Buf; + // CHECK: VarDecl {{.*}} ea 'EmptyArrayTypedef':'float[10][0]' + EmptyArrayTypedef ea; + // CHECK: CXXRecordDecl {{.*}} implicit class __layout_CB_5 definition + // CHECK-NOT: FieldDecl +} + +// check host layout struct with compatible base struct +// CHECK: HLSLBufferDecl {{.*}} line:160:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: VarDecl {{.*}} s8 'F' + F s8; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_6 definition + // CHECK: FieldDecl {{.*}} s8 '__layout_F' +} + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_F definition + // CHECK: public 'A' +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_F), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_CB_6), ""); + +// anonymous structs +// CHECK: HLSLBufferDecl {{.*}} line:175:9 cbuffer CB +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB { + // CHECK: CXXRecordDecl {{.*}} struct definition + struct { + // CHECK: FieldDecl {{.*}} e 'float' + float e; + // CHECK: FieldDecl {{.*}} c 'int[0][1]' + int c[0][1]; + // CHECK: FieldDecl {{.*}} f 'RWBuffer':'hlsl::RWBuffer' + RWBuffer f; + } s9; + // CHECK: VarDecl {{.*}} s9 'struct (unnamed struct at {{.*}}cbuffer.hlsl:177:3 + // CHECK: CXXRecordDecl {{.*}} struct definition + struct { + // CHECK: FieldDecl {{.*}} g 'float' + float g; + // CHECK: FieldDecl {{.*}} f 'RWBuffer':'hlsl::RWBuffer' + RWBuffer f; + } s10; + // CHECK: VarDecl {{.*}} s10 'struct (unnamed struct at {{.*}}cbuffer.hlsl:187:3 + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_anon definition + // CHECK: FieldDecl {{.*}} e 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_anon_1 definition + // CHECK: FieldDecl {{.*}} g 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_7 definition + // CHECK: FieldDecl {{.*}} s9 '__layout_anon' + // CHECK: FieldDecl {{.*}} s10 '__layout_anon_1' +} +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_anon), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_anon_1), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_7), ""); + +// Add uses for the constant buffer declarations so they are not optimized away +export float foo() { + return a1 + a2 + s1.a + s4.b + s5.c + s8.a + s9.e; +} diff --git a/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl b/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl new file mode 100644 index 0000000000000..4b1bbea736f85 --- /dev/null +++ b/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s + +// CHECK: CXXRecordDecl {{.*}} struct EmptyStruct definition +struct EmptyStruct { +}; + +// CHECK: NamespaceDecl {{.*}} NS1 +namespace NS1 { + // CHECK: CXXRecordDecl {{.*}} struct Foo definition + struct Foo { + float a; + EmptyStruct es; + }; + + // CHECK: CXXRecordDecl {{.*}} struct Bar definition + struct Bar { + // CHECK: CXXRecordDecl {{.*}} struct Foo definition + struct Foo { + int b; + EmptyStruct es; + }; + // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: FieldDecl {{.*}} b 'int' + }; + // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: FieldDecl {{.*}} a 'float' +} + +struct Foo { + double c; + EmptyStruct es; +}; + +// CHECK: HLSLBufferDecl {{.*}} line:37:9 cbuffer CB1 +// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer +cbuffer CB1 { + // CHECK: VarDecl {{.*}} foo1 'Foo' + Foo foo1; + // CHECK: VarDecl {{.*}} foo2 'NS1::Foo' + NS1::Foo foo2; + // CHECK: VarDecl {{.*}} foo3 'NS1::Bar::Foo' + NS1::Bar::Foo foo3; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB1 definition + // CHECK: FieldDecl {{.*}} foo1 '__layout_Foo' + // CHECK: FieldDecl {{.*}} foo2 'NS1::__layout_Foo' + // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__layout_Foo' +} +// CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition +// CHECK: FieldDecl {{.*}} c 'double' + +struct CB1ExpectedShape { + double a1; + float a2; + int a; +}; +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB1ExpectedShape, __layout_CB1), ""); + +namespace NS2 { + struct Foo { + float d[4]; + EmptyStruct es; + }; + // CHECK: HLSLBufferDecl {{.*}} line:67:11 cbuffer CB2 + // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer + // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer + cbuffer CB2 { + // CHECK: VarDecl {{.*}} foo0 '::Foo':'Foo' + ::Foo foo0; + // CHECK: VarDecl {{.*}} foo1 'Foo':'NS2::Foo' + Foo foo1; + // CHECK: VarDecl {{.*}} foo2 'NS1::Foo' + NS1::Foo foo2; + // CHECK: VarDecl {{.*}} foo3 'NS1::Bar::Foo' + NS1::Bar::Foo foo3; + // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB2 definition + // CHECK: FieldDecl {{.*}} foo0 '__layout_Foo' + // CHECK: FieldDecl {{.*}} foo1 'NS2::__layout_Foo' + // CHECK: FieldDecl {{.*}} foo2 'NS1::__layout_Foo' + // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__layout_Foo' + } + // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: FieldDecl {{.*}} d 'float[4]' +} + +struct CB2ExpectedShape { + double a1; + float d[4]; + float a2; + int a; +}; +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB2ExpectedShape, NS2::__layout_CB2), ""); + +// Add uses for the constant buffer declarations so they are not optimized away +// CHECK: ExportDecl +export float f() { + return foo2.a + NS2::foo2.a; +} diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl deleted file mode 100644 index 5e558354cd3a0..0000000000000 --- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s - -// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB -// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <> Implicit CBuffer -// CHECK-NEXT:HLSLResourceAttr {{.*}} <> Implicit CBuffer -// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float' -cbuffer CB { - float a; -} - -// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB -// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <> Implicit SRV -// CHECK-NEXT:HLSLResourceAttr {{.*}} <> Implicit TBuffer -// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float' -tbuffer TB { - float b; -} - -float foo() { -// CHECK: BinaryOperator 0x{{[0-9a-f]+}} 'float' '+' -// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' -// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' -// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' -// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' - return a + b; -} diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl index 281d8be8addf0..3eabbb1f8ae22 100644 --- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl +++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl @@ -15,20 +15,27 @@ tbuffer B { float foo() { return a + b; } + // Make sure cbuffer/tbuffer works for PCH. -// CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported cbuffer A -// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <> Implicit CBuffer -// CHECK-NEXT:HLSLResourceAttr {{.*}} <> Implicit CBuffer -// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] col:9 imported used a 'float' -// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} line:11:9 imported tbuffer B -// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <> Implicit SRV -// CHECK-NEXT:HLSLResourceAttr {{.*}} <> Implicit TBuffer -// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] col:9 imported used b 'float' -// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} line:15:7 imported foo 'float ()' -// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} -// CHECK-NEXT:ReturnStmt 0x{{[0-9a-f]+}} -// CHECK-NEXT:BinaryOperator 0x{{[0-9a-f]+}} 'float' '+' -// CHECK-NEXT:ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' -// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' -// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' -// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' +// CHECK: HLSLBufferDecl {{.*}} line:7:9 imported cbuffer A +// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer +// CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit CBuffer +// CHECK-NEXT: VarDecl 0x[[A:[0-9a-f]+]] {{.*}} imported used a 'float' +// CHECK-NEXT: CXXRecordDecl {{.*}} imported implicit class __layout_A definition +// CHECK: FieldDecl {{.*}} imported a 'float' + +// CHECK: HLSLBufferDecl {{.*}} line:11:9 imported tbuffer B +// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit SRV +// CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit TBuffer +// CHECK-NEXT: VarDecl 0x[[B:[0-9a-f]+]] {{.*}} imported used b 'float' +// CHECK-NEXT: CXXRecordDecl 0x{{[0-9a-f]+}} {{.*}} imported implicit class __layout_B definition +// CHECK: FieldDecl 0x{{[0-9a-f]+}} {{.*}} imported b 'float' + +// CHECK-NEXT: FunctionDecl {{.*}} line:15:7 imported foo 'float ()' +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-NEXT: ReturnStmt {{.*}} +// CHECK-NEXT: BinaryOperator {{.*}} 'float' '+' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue Var 0x[[B]] 'b' 'float'