Skip to content

Commit 71ddb5a

Browse files
committed
[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
1 parent cd264f0 commit 71ddb5a

File tree

9 files changed

+509
-35
lines changed

9 files changed

+509
-35
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4967,6 +4967,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
49674967
SourceLocation getRBraceLoc() const { return RBraceLoc; }
49684968
void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
49694969
bool isCBuffer() const { return IsCBuffer; }
4970+
const Type *getResourceHandleType() const;
49704971

49714972
// Implement isa/cast/dyncast/etc.
49724973
static bool classof(const Decl *D) { return classofKind(D->getKind()); }

clang/lib/AST/Decl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5693,6 +5693,19 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
56935693
SourceLocation(), SourceLocation());
56945694
}
56955695

5696+
const Type *HLSLBufferDecl::getResourceHandleType() const {
5697+
// Resource handle is the last decl in the HLSLBufferDecl.
5698+
// If it is not present, it probably means the buffer is empty.
5699+
if (VarDecl *VD = llvm::dyn_cast_or_null<VarDecl>(LastDecl)) {
5700+
const Type *Ty = VD->getType().getTypePtr();
5701+
if (Ty->isHLSLAttributedResourceType()) {
5702+
assert(VD->getNameAsString() == "__handle");
5703+
return Ty;
5704+
}
5705+
}
5706+
return nullptr;
5707+
}
5708+
56965709
//===----------------------------------------------------------------------===//
56975710
// ImportDecl Implementation
56985711
//===----------------------------------------------------------------------===//

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,12 @@ void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
159159
CB.Constants.emplace_back(std::make_pair(GV, LowerBound));
160160
}
161161

162-
void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) {
163-
for (Decl *it : DC->decls()) {
162+
void CGHLSLRuntime::addBufferDecls(const HLSLBufferDecl *D, Buffer &CB) {
163+
for (Decl *it : D->decls()) {
164164
if (auto *ConstDecl = dyn_cast<VarDecl>(it)) {
165-
addConstant(ConstDecl, CB);
165+
if (ConstDecl->getType().getTypePtr() != D->getResourceHandleType()) {
166+
addConstant(ConstDecl, CB);
167+
}
166168
} else if (isa<CXXRecordDecl, EmptyDecl>(it)) {
167169
// Nothing to do for this declaration.
168170
} else if (isa<FunctionDecl>(it)) {

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class CGHLSLRuntime {
169169
llvm::hlsl::ElementType ET,
170170
BufferResBinding &Binding);
171171
void addConstant(VarDecl *D, Buffer &CB);
172-
void addBufferDecls(const DeclContext *DC, Buffer &CB);
172+
void addBufferDecls(const HLSLBufferDecl *D, Buffer &CB);
173173
llvm::Triple::ArchType getArch();
174174
llvm::SmallVector<Buffer> Buffers;
175175

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,26 @@
2121
#include "clang/AST/TypeLoc.h"
2222
#include "clang/Basic/Builtins.h"
2323
#include "clang/Basic/DiagnosticSema.h"
24+
#include "clang/Basic/IdentifierTable.h"
2425
#include "clang/Basic/LLVM.h"
2526
#include "clang/Basic/SourceLocation.h"
2627
#include "clang/Basic/TargetInfo.h"
2728
#include "clang/Sema/Initialization.h"
29+
#include "clang/Sema/Lookup.h"
2830
#include "clang/Sema/ParsedAttr.h"
2931
#include "clang/Sema/Sema.h"
3032
#include "clang/Sema/Template.h"
3133
#include "llvm/ADT/STLExtras.h"
3234
#include "llvm/ADT/SmallVector.h"
3335
#include "llvm/ADT/StringExtras.h"
3436
#include "llvm/ADT/StringRef.h"
37+
#include "llvm/ADT/Twine.h"
3538
#include "llvm/Support/Casting.h"
3639
#include "llvm/Support/DXILABI.h"
3740
#include "llvm/Support/ErrorHandling.h"
3841
#include "llvm/TargetParser/Triple.h"
3942
#include <iterator>
43+
#include <string>
4044
#include <utility>
4145

4246
using namespace clang;
@@ -253,12 +257,253 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
253257
}
254258
}
255259

260+
// Returns true if the array has a zero size = if any of the dimensions is 0
261+
static bool isZeroSizedArray(const ConstantArrayType *CAT) {
262+
while (CAT && !CAT->isZeroSize())
263+
CAT = dyn_cast<ConstantArrayType>(
264+
CAT->getElementType()->getUnqualifiedDesugaredType());
265+
return CAT != nullptr;
266+
}
267+
268+
// Returns true if the struct can be used inside HLSL Buffer which means
269+
// that it does not contain intangible types, empty structs, zero-sized arrays,
270+
// and the same is true for its base or embedded structs.
271+
bool isStructHLSLBufferCompatible(const CXXRecordDecl *RD) {
272+
if (RD->getTypeForDecl()->isHLSLIntangibleType() ||
273+
(RD->field_empty() && RD->getNumBases() == 0))
274+
return false;
275+
// check fields
276+
for (const FieldDecl *Field : RD->fields()) {
277+
QualType Ty = Field->getType();
278+
if (Ty->isRecordType()) {
279+
if (!isStructHLSLBufferCompatible(Ty->getAsCXXRecordDecl()))
280+
return false;
281+
} else if (Ty->isConstantArrayType()) {
282+
if (isZeroSizedArray(cast<ConstantArrayType>(Ty)))
283+
return false;
284+
}
285+
}
286+
// check bases
287+
for (const CXXBaseSpecifier &Base : RD->bases())
288+
if (!isStructHLSLBufferCompatible(Base.getType()->getAsCXXRecordDecl()))
289+
return false;
290+
return true;
291+
}
292+
293+
static CXXRecordDecl *findRecordDecl(Sema &S, IdentifierInfo *II,
294+
DeclContext *DC) {
295+
DeclarationNameInfo NameInfo =
296+
DeclarationNameInfo(DeclarationName(II), SourceLocation());
297+
LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
298+
S.LookupName(R, S.getScopeForContext(DC));
299+
if (R.isSingleResult())
300+
return R.getAsSingle<CXXRecordDecl>();
301+
return nullptr;
302+
}
303+
304+
// Creates a name for buffer layout struct using the provide name base.
305+
// If the name must be unique (not previously defined), a suffix is added
306+
// until a unique name is found.
307+
static IdentifierInfo *getHostLayoutStructName(Sema &S,
308+
IdentifierInfo *NameBaseII,
309+
bool MustBeUnique,
310+
DeclContext *DC) {
311+
ASTContext &AST = S.getASTContext();
312+
std::string NameBase;
313+
if (NameBaseII) {
314+
NameBase = NameBaseII->getName().str();
315+
} else {
316+
// anonymous struct
317+
NameBase = "anon";
318+
MustBeUnique = true;
319+
}
320+
321+
std::string Name = "__hostlayout.struct." + NameBase;
322+
IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier);
323+
if (!MustBeUnique)
324+
return II;
325+
326+
unsigned suffix = 0;
327+
while (true) {
328+
if (suffix != 0)
329+
II = &AST.Idents.get((llvm::Twine(Name) + "." + Twine(suffix)).str(),
330+
tok::TokenKind::identifier);
331+
if (!findRecordDecl(S, II, DC))
332+
return II;
333+
// declaration with that name already exists - increment suffix and try
334+
// again until unique name is found
335+
suffix++;
336+
};
337+
}
338+
339+
// Returns true if the record type is an HLSL resource class
340+
static bool isResourceRecordType(const Type *Ty) {
341+
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
342+
}
343+
344+
static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl *StructDecl,
345+
HLSLBufferDecl *BufDecl);
346+
347+
// Creates a field declaration of given name and type for HLSL buffer layout
348+
// struct. Returns nullptr if the type cannot be use in HLSL Buffer layout.
349+
static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty,
350+
IdentifierInfo *II,
351+
CXXRecordDecl *LayoutStruct,
352+
HLSLBufferDecl *BufDecl) {
353+
if (Ty->isRecordType()) {
354+
if (isResourceRecordType(Ty))
355+
return nullptr;
356+
CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
357+
if (!isStructHLSLBufferCompatible(RD)) {
358+
RD = createHostLayoutStruct(S, RD, BufDecl);
359+
if (!RD)
360+
return nullptr;
361+
Ty = RD->getTypeForDecl();
362+
}
363+
} else if (Ty->isConstantArrayType()) {
364+
if (isZeroSizedArray(cast<ConstantArrayType>(Ty)))
365+
return nullptr;
366+
}
367+
QualType QT = QualType(Ty, 0);
368+
ASTContext &AST = S.getASTContext();
369+
TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation());
370+
auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(),
371+
SourceLocation(), II, QT, TSI, nullptr, false,
372+
InClassInitStyle::ICIS_NoInit);
373+
Field->setAccess(AccessSpecifier::AS_private);
374+
return Field;
375+
}
376+
377+
// Creates host layout struct for a struct included in HLSL Buffer.
378+
// The layout struct will include only fields that are allowed in HLSL buffer.
379+
// These fields will be filtered out:
380+
// - resource classes
381+
// - empty structs
382+
// - zero-sized arrays
383+
// Returns nullptr if the resulting layout struct would be empty.
384+
static CXXRecordDecl *createHostLayoutStruct(Sema &S, CXXRecordDecl *StructDecl,
385+
HLSLBufferDecl *BufDecl) {
386+
assert(!isStructHLSLBufferCompatible(StructDecl) &&
387+
"struct is already HLSL buffer compatible");
388+
389+
ASTContext &AST = S.getASTContext();
390+
DeclContext *DC = StructDecl->getDeclContext();
391+
IdentifierInfo *II = getHostLayoutStructName(
392+
S, StructDecl->getIdentifier(), false, BufDecl->getDeclContext());
393+
394+
// reuse existing if the layout struct if it already exists
395+
if (CXXRecordDecl *RD = findRecordDecl(S, II, DC))
396+
return RD;
397+
398+
CXXRecordDecl *LS =
399+
CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl,
400+
SourceLocation(), SourceLocation(), II);
401+
LS->setImplicit(true);
402+
LS->startDefinition();
403+
404+
// copy base struct, create HLSL Buffer compatible version if needed
405+
if (unsigned NumBases = StructDecl->getNumBases()) {
406+
assert(NumBases == 1 && "HLSL supports only one base type");
407+
CXXBaseSpecifier Base = *StructDecl->bases_begin();
408+
CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
409+
if (!isStructHLSLBufferCompatible(BaseDecl)) {
410+
BaseDecl = createHostLayoutStruct(S, BaseDecl, BufDecl);
411+
if (BaseDecl) {
412+
TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(
413+
QualType(BaseDecl->getTypeForDecl(), 0));
414+
Base = CXXBaseSpecifier(SourceRange(), false, StructDecl->isClass(),
415+
AS_none, TSI, SourceLocation());
416+
}
417+
}
418+
if (BaseDecl) {
419+
const CXXBaseSpecifier *BasesArray[1] = {&Base};
420+
LS->setBases(BasesArray, 1);
421+
}
422+
}
423+
424+
// filter struct fields
425+
for (const FieldDecl *FD : StructDecl->fields()) {
426+
const Type *Ty = FD->getType()->getUnqualifiedDesugaredType();
427+
if (FieldDecl *NewFD = createFieldForHostLayoutStruct(
428+
S, Ty, FD->getIdentifier(), LS, BufDecl))
429+
LS->addDecl(NewFD);
430+
}
431+
LS->completeDefinition();
432+
433+
if (LS->field_empty() && LS->getNumBases() == 0)
434+
return nullptr;
435+
BufDecl->addDecl(LS);
436+
return LS;
437+
}
438+
439+
// Creates host layout struct for HLSL Buffer. The struct will include only
440+
// fields of types that are allowed in HLSL buffer and it will filter out:
441+
// - static variable declarations
442+
// - resource classes
443+
// - empty structs
444+
// - zero-sized arrays
445+
// - non-variable declarations
446+
static CXXRecordDecl *createHostLayoutStructForBuffer(Sema &S,
447+
HLSLBufferDecl *BufDecl) {
448+
ASTContext &AST = S.getASTContext();
449+
IdentifierInfo *II = getHostLayoutStructName(S, BufDecl->getIdentifier(),
450+
true, BufDecl->getDeclContext());
451+
452+
CXXRecordDecl *LS =
453+
CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl,
454+
SourceLocation(), SourceLocation(), II);
455+
LS->setImplicit(true);
456+
LS->startDefinition();
457+
458+
for (const Decl *D : BufDecl->decls()) {
459+
const VarDecl *VD = dyn_cast<VarDecl>(D);
460+
if (!VD || VD->getStorageClass() == SC_Static)
461+
continue;
462+
const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
463+
if (FieldDecl *FD = createFieldForHostLayoutStruct(
464+
S, Ty, VD->getIdentifier(), LS, BufDecl))
465+
LS->addDecl(FD);
466+
}
467+
LS->completeDefinition();
468+
BufDecl->addDecl(LS);
469+
return LS;
470+
}
471+
472+
// Creates a "__handle" declaration for the HLSL Buffer type
473+
// with the corresponding HLSL resource type and adds it to the HLSLBufferDecl
474+
static void createHLSLBufferHandle(Sema &S, HLSLBufferDecl *BufDecl,
475+
CXXRecordDecl *LayoutStruct) {
476+
ASTContext &AST = S.getASTContext();
477+
478+
HLSLAttributedResourceType::Attributes ResAttrs(
479+
BufDecl->isCBuffer() ? ResourceClass::CBuffer : ResourceClass::SRV, false,
480+
false);
481+
QualType ResHandleTy = AST.getHLSLAttributedResourceType(
482+
AST.HLSLResourceTy, QualType(LayoutStruct->getTypeForDecl(), 0),
483+
ResAttrs);
484+
485+
IdentifierInfo *II = &AST.Idents.get("__handle", tok::TokenKind::identifier);
486+
VarDecl *VD = VarDecl::Create(
487+
BufDecl->getASTContext(), BufDecl, SourceLocation(), SourceLocation(), II,
488+
ResHandleTy, AST.getTrivialTypeSourceInfo(ResHandleTy, SourceLocation()),
489+
SC_None);
490+
BufDecl->addDecl(VD);
491+
}
492+
493+
// Handle end of cbuffer/tbuffer declaration
256494
void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
257495
auto *BufDecl = cast<HLSLBufferDecl>(Dcl);
258496
BufDecl->setRBraceLoc(RBrace);
259497

260498
validatePackoffset(SemaRef, BufDecl);
261499

500+
// create buffer layout struct
501+
CXXRecordDecl *LayoutStruct =
502+
createHostLayoutStructForBuffer(SemaRef, BufDecl);
503+
504+
// create buffer resource handle
505+
createHLSLBufferHandle(SemaRef, BufDecl, LayoutStruct);
506+
262507
SemaRef.PopDeclContext();
263508
}
264509

clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,23 @@ tbuffer B {
4444
// AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
4545
// AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
4646
// AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
47-
// AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
47+
// AST-NEXT:-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
48+
// AST-NEXT:CXXRecordDecl 0x[[CB:[0-9a-f]+]] {{.*}} implicit class __hostlayout.struct.A definition
49+
// AST:FieldDecl 0x[[CB:[0-9a-f]+]] {{.*}} a 'float'
50+
// AST-NEXT:FieldDecl 0x[[CB:[0-9a-f]+]] {{.*}} b 'int'
51+
// AST-NEXT:VarDecl 0x[[CB:[0-9a-f]+]] {{.*}} __handle '__hlsl_resource_t
52+
// AST-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]] [[hlsl::contained_type(__hostlayout.struct.A)]]'
53+
4854
// AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
4955
// AST-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit SRV
5056
// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
5157
// AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
5258
// AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
5359
// AST-NEXT: `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
5460
// AST-NEXT:-VarDecl {{.*}}<line:33:5, col:11> col:11 c 'float'
55-
// AST-NEXT:`-VarDecl {{.*}} <line:37:5, col:9> col:9 d 'int'
61+
// AST-NEXT:-VarDecl {{.*}} <line:37:5, col:9> col:9 d 'int'
62+
// AST-NEXT:CXXRecordDecl 0x[[CB:[0-9a-f]+]] {{.*}} implicit class __hostlayout.struct.B definition
63+
// AST:FieldDecl 0x[[CB:[0-9a-f]+]] {{.*}} c 'float'
64+
// AST-NEXT:FieldDecl 0x[[CB:[0-9a-f]+]] {{.*}} d 'int'
65+
// AST-NEXT:VarDecl 0x[[CB:[0-9a-f]+]] {{.*}} __handle '__hlsl_resource_t
66+
// AST-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] [[hlsl::contained_type(__hostlayout.struct.B)]]'

0 commit comments

Comments
 (0)