Skip to content

[HLSL] Constant buffer layout struct update #124840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14183,6 +14183,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().OpenCL &&
Var->getType().getAddressSpace() == LangAS::opencl_local)
return;

// In HLSL, objects in the hlsl_constat address space are initialized
// externaly, so don't synthesize an implicit initializer.
if (getLangOpts().HLSL &&
Var->getType().getAddressSpace() == LangAS::hlsl_constant)
return;

// C++03 [dcl.init]p9:
// If no initializer is specified for an object, and the
// object is of (possibly cv-qualified) non-POD class type (or
Expand Down
32 changes: 19 additions & 13 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,11 @@ static bool isZeroSizedArray(const ConstantArrayType *CAT) {
return CAT != nullptr;
}

// Returns true if the record type is an HLSL resource class
static bool isResourceRecordType(const Type *Ty) {
// Returns true if the record type is an HLSL resource class or an array of
// HLSL resource classes
static bool isResourceRecordTypeOrArrayOf(const Type *Ty) {
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
Ty = CAT->getArrayElementTypeNoTypeQual();
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
}

Expand All @@ -279,11 +282,10 @@ static bool isResourceRecordType(const Type *Ty) {
// 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 (isResourceRecordTypeOrArrayOf(Ty))
return true;
if (Ty->isRecordType())
return Ty->getAsCXXRecordDecl()->isEmpty();
if (Ty->isConstantArrayType() &&
isZeroSizedArray(cast<ConstantArrayType>(Ty)))
return true;
Expand Down Expand Up @@ -339,7 +341,7 @@ static IdentifierInfo *getHostLayoutStructName(Sema &S, NamedDecl *BaseDecl,
ASTContext &AST = S.getASTContext();

IdentifierInfo *NameBaseII = BaseDecl->getIdentifier();
llvm::SmallString<64> Name("__layout_");
llvm::SmallString<64> Name("__cblayout_");
if (NameBaseII) {
Name.append(NameBaseII->getName());
} else {
Expand Down Expand Up @@ -393,7 +395,7 @@ static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty,
auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
SourceLocation(), II, QT, TSI, nullptr, false,
InClassInitStyle::ICIS_NoInit);
Field->setAccess(AccessSpecifier::AS_private);
Field->setAccess(AccessSpecifier::AS_public);
return Field;
}

Expand All @@ -417,9 +419,11 @@ static CXXRecordDecl *createHostLayoutStruct(Sema &S,
if (CXXRecordDecl *RD = findRecordDeclInContext(II, DC))
return RD;

CXXRecordDecl *LS = CXXRecordDecl::Create(
AST, TagDecl::TagKind::Class, DC, SourceLocation(), SourceLocation(), II);
CXXRecordDecl *LS =
CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, DC, SourceLocation(),
SourceLocation(), II);
LS->setImplicit(true);
LS->addAttr(PackedAttr::CreateImplicit(AST));
LS->startDefinition();

// copy base struct, create HLSL Buffer compatible version if needed
Expand Down Expand Up @@ -472,14 +476,16 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
IdentifierInfo *II = getHostLayoutStructName(S, BufDecl, true);

CXXRecordDecl *LS =
CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl,
CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, BufDecl,
SourceLocation(), SourceLocation(), II);
LS->addAttr(PackedAttr::CreateImplicit(AST));
LS->setImplicit(true);
LS->startDefinition();

for (Decl *D : BufDecl->decls()) {
VarDecl *VD = dyn_cast<VarDecl>(D);
if (!VD || VD->getStorageClass() == SC_Static)
if (!VD || VD->getStorageClass() == SC_Static ||
VD->getType().getAddressSpace() == LangAS::hlsl_groupshared)
continue;
const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
if (FieldDecl *FD =
Expand Down
3 changes: 0 additions & 3 deletions clang/test/AST/HLSL/ast-dump-comment-cbuffer.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,3 @@ cbuffer A {
// AST-NEXT: TextComment {{.*}} Text=" CBuffer decl."
// AST-NEXT: VarDecl {{.*}} a 'hlsl_constant float'
// AST-NEXT: VarDecl {{.*}} b 'hlsl_constant int'
// AST-NEXT: CXXRecordDecl {{.*}} implicit class __layout_A definition
// AST: FieldDecl {{.*}} a 'float'
// AST-NEXT: FieldDecl {{.*}} b 'int'
146 changes: 82 additions & 64 deletions clang/test/AST/HLSL/cbuffer.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -48,94 +48,106 @@ struct TwoFloats {
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} col:9 used a1 'hlsl_constant float'
// CHECK: VarDecl {{.*}} used a1 'hlsl_constant float'
float a1;
// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB definition
// CHECK: FieldDecl {{.*}} a1 'float'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} a1 'float'
}
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_CB), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_CB), "");

// Check that buffer layout struct does not include resources or empty types
// CHECK: HLSLBufferDecl {{.*}} line:62:9 cbuffer CB
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} col:9 used a2 'hlsl_constant float'
// CHECK: VarDecl {{.*}} used a2 'hlsl_constant float'
float a2;
// CHECK: VarDecl {{.*}} col:19 b2 'RWBuffer<float>':'hlsl::RWBuffer<float>'
// CHECK: VarDecl {{.*}} b2 'RWBuffer<float>':'hlsl::RWBuffer<float>'
RWBuffer<float> b2;
// CHECK: VarDecl {{.*}} col:15 c2 'EmptyStruct'
// CHECK: VarDecl {{.*}} c2 'EmptyStruct'
EmptyStruct c2;
// CHECK: VarDecl {{.*}} col:9 d2 'float[0]'
// CHECK: VarDecl {{.*}} d2 'float[0]'
float d2[0];
// CHECK: VarDecl {{.*}} col:9 e2 'hlsl_constant float'
// CHECK: VarDecl {{.*}} f2 'RWBuffer<float>[2]'
RWBuffer<float> f2[2];
// CHECK: VarDecl {{.*}} g2 'groupshared float'
groupshared float g2;
// CHECK: VarDecl {{.*}} e2 'hlsl_constant float'
float e2;
// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_1 definition
// CHECK: FieldDecl {{.*}} a2 'float'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_1 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} a2 'float'
// CHECK-NEXT: FieldDecl {{.*}} e2 'float'
}
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_1), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_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: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} col:5 used s1 'hlsl_constant A'
// CHECK: VarDecl {{.*}} used s1 'hlsl_constant A'
A s1;
// CHECK: VarDecl {{.*}} col:5 s2 'hlsl_constant B'
// CHECK: VarDecl {{.*}} s2 'hlsl_constant B'
B s2;
// CHECK: VarDecl {{.*}} col:12 s3 'CTypedef':'C'
// CHECK: VarDecl {{.*}} 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 struct __cblayout_CB_2 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} s1 'A'
// CHECK-NEXT: FieldDecl {{.*}} s2 '__cblayout_B'
}
// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_B definition
// CHECK: FieldDecl {{.*}} a 'float'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_B definition
// CHECK: PackedAttr
// CHECK-NEXT: 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), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_B), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_2), "");

// check that layout struct is created for D because of its base struct
// CHECK: HLSLBufferDecl {{.*}} line:104:9 cbuffer CB
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} s4 'hlsl_constant D'
D s4;
// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_3 definition
// CHECK: FieldDecl {{.*}} s4 '__layout_D'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_3 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} s4 '__cblayout_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: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_D definition
// CHECK: public '__cblayout_B'
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} b 'float'
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_D), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_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
// defined '__cblayout_B'
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} s5 'hlsl_constant E'
E s5;
// CHECK: VarDecl {{.*}} s6 'hlsl_constant BTypedef':'hlsl_constant 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 struct __cblayout_CB_4 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} s5 '__cblayout_E'
// CHECK-NEXT: FieldDecl {{.*}} s6 '__cblayout_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: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_E definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} c 'float'
// CHECK-NOT: CXXRecordDecl {{.*}} struct __cblayout_B definition
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_E), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_4), "");

// check that this produces empty layout struct
// CHECK: HLSLBufferDecl {{.*}} line:141:9 cbuffer CB
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
Expand All @@ -149,27 +161,30 @@ cbuffer CB {
RWBuffer<float> Buf;
// CHECK: VarDecl {{.*}} ea 'EmptyArrayTypedef':'float[10][0]'
EmptyArrayTypedef ea;
// CHECK: CXXRecordDecl {{.*}} implicit class __layout_CB_5 definition
// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_CB_5 definition
// CHECK: PackedAttr
// CHECK-NOT: FieldDecl
}

// check host layout struct with compatible base struct
// CHECK: HLSLBufferDecl {{.*}} line:160:9 cbuffer CB
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
// CHECK: VarDecl {{.*}} s8 'hlsl_constant F'
F s8;
// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_6 definition
// CHECK: FieldDecl {{.*}} s8 '__layout_F'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_6 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} s8 '__cblayout_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), "");
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_F definition
// CHECK: public 'A'
// CHECK: PackedAttr
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_F), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_CB_6), "");

// anonymous structs
// CHECK: HLSLBufferDecl {{.*}} line:175:9 cbuffer CB
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB
// CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer
cbuffer CB {
Expand All @@ -182,26 +197,29 @@ cbuffer CB {
// CHECK: FieldDecl {{.*}} f 'RWBuffer<float>':'hlsl::RWBuffer<float>'
RWBuffer<float> f;
} s9;
// CHECK: VarDecl {{.*}} s9 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:177:3
// CHECK: VarDecl {{.*}} s9 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:[[# @LINE - 8]]:3
// CHECK: CXXRecordDecl {{.*}} struct definition
struct {
// CHECK: FieldDecl {{.*}} g 'float'
float g;
// CHECK: FieldDecl {{.*}} f 'RWBuffer<float>':'hlsl::RWBuffer<float>'
RWBuffer<float> f;
} s10;
// CHECK: VarDecl {{.*}} s10 'hlsl_constant 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'
// CHECK: VarDecl {{.*}} s10 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:[[# @LINE - 6]]:3
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_anon definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} e 'float'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_anon_1 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} g 'float'
// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_7 definition
// CHECK: PackedAttr
// CHECK-NEXT: FieldDecl {{.*}} s9 '__cblayout_anon'
// CHECK-NEXT: FieldDecl {{.*}} s10 '__cblayout_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), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_anon), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_anon_1), "");
_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_7), "");

// Add uses for the constant buffer declarations so they are not optimized away
export float foo() {
Expand Down
Loading