Skip to content

Commit 42bb34f

Browse files
committed
[HLSL] Implement default constant buffer $Globals
All variable declarations in the global scope that are not resources, static or empty are implicitly added to implicit constant buffer `$Globals`. Fixes #123801
1 parent a8cdd45 commit 42bb34f

File tree

10 files changed

+242
-47
lines changed

10 files changed

+242
-47
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5038,6 +5038,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50385038
// LayoutStruct - Layout struct for the buffer
50395039
CXXRecordDecl *LayoutStruct;
50405040

5041+
// For default (implicit) constant buffer, a lisf of references of global
5042+
// decls that belong to the buffer. The decls are already parented by the
5043+
// translation unit context.
5044+
SmallVector<Decl *> DefaultBufferDecls;
5045+
50415046
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
50425047
IdentifierInfo *ID, SourceLocation IDLoc,
50435048
SourceLocation LBrace);
@@ -5047,6 +5052,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50475052
bool CBuffer, SourceLocation KwLoc,
50485053
IdentifierInfo *ID, SourceLocation IDLoc,
50495054
SourceLocation LBrace);
5055+
static HLSLBufferDecl *CreateDefaultCBuffer(ASTContext &C,
5056+
DeclContext *LexicalParent);
50505057
static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
50515058

50525059
SourceRange getSourceRange() const override LLVM_READONLY {
@@ -5061,6 +5068,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50615068
bool hasPackoffset() const { return HasPackoffset; }
50625069
const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; }
50635070
void addLayoutStruct(CXXRecordDecl *LS);
5071+
void addDefaultBufferDecl(Decl *D);
50645072

50655073
// Implement isa/cast/dyncast/etc.
50665074
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
@@ -5072,6 +5080,20 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50725080
return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(DC));
50735081
}
50745082

5083+
// Iterator for the buffer decls. Concatenates the list of decls parented
5084+
// by this HLSLBufferDecl with the list of default buffer decls.
5085+
using buffer_decl_iterator =
5086+
llvm::concat_iterator<Decl *const, SmallVector<Decl *>::const_iterator,
5087+
decl_iterator>;
5088+
using buffer_decl_range = llvm::iterator_range<buffer_decl_iterator>;
5089+
5090+
buffer_decl_range buffer_decls() const {
5091+
return buffer_decl_range(buffer_decls_begin(), buffer_decls_end());
5092+
}
5093+
buffer_decl_iterator buffer_decls_begin() const;
5094+
buffer_decl_iterator buffer_decls_end() const;
5095+
bool buffer_decls_empty();
5096+
50755097
friend class ASTDeclReader;
50765098
friend class ASTDeclWriter;
50775099
};

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ class SemaHLSL : public SemaBase {
103103
HLSLParamModifierAttr::Spelling Spelling);
104104
void ActOnTopLevelFunction(FunctionDecl *FD);
105105
void ActOnVariableDeclarator(VarDecl *VD);
106+
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
106107
void CheckEntryPoint(FunctionDecl *FD);
107108
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
108109
const HLSLAnnotationAttr *AnnotationAttr);
109110
void DiagnoseAttrStageMismatch(
110111
const Attr *A, llvm::Triple::EnvironmentType Stage,
111112
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
112-
void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);
113113

114114
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
115115
QualType LHSType, QualType RHSType,
@@ -159,11 +159,16 @@ class SemaHLSL : public SemaBase {
159159
// List of all resource bindings
160160
ResourceBindings Bindings;
161161

162+
// default constant buffer $Globals
163+
HLSLBufferDecl *DefaultCBuffer;
164+
162165
private:
163166
void collectResourcesOnVarDecl(VarDecl *D);
164167
void collectResourcesOnUserRecordDecl(const VarDecl *VD,
165168
const RecordType *RT);
166169
void processExplicitBindingsOnDecl(VarDecl *D);
170+
171+
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
167172
};
168173

169174
} // namespace clang

clang/lib/AST/Decl.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "llvm/ADT/SmallVector.h"
5858
#include "llvm/ADT/StringRef.h"
5959
#include "llvm/ADT/StringSwitch.h"
60+
#include "llvm/ADT/iterator_range.h"
6061
#include "llvm/Support/Casting.h"
6162
#include "llvm/Support/ErrorHandling.h"
6263
#include "llvm/Support/raw_ostream.h"
@@ -5741,10 +5742,22 @@ HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
57415742
return Result;
57425743
}
57435744

5745+
HLSLBufferDecl *
5746+
HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C,
5747+
DeclContext *LexicalParent) {
5748+
DeclContext *DC = LexicalParent;
5749+
IdentifierInfo *II = &C.Idents.get("$Globals", tok::TokenKind::identifier);
5750+
HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl(
5751+
DC, true, SourceLocation(), II, SourceLocation(), SourceLocation());
5752+
Result->setImplicit(true);
5753+
return Result;
5754+
}
5755+
57445756
HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
57455757
GlobalDeclID ID) {
5746-
return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
5747-
SourceLocation(), SourceLocation());
5758+
return new (C, ID)
5759+
HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
5760+
SourceLocation(), SourceLocation());
57485761
}
57495762

57505763
void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
@@ -5753,6 +5766,30 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
57535766
addDecl(LS);
57545767
}
57555768

5769+
void HLSLBufferDecl::addDefaultBufferDecl(Decl *D) {
5770+
assert(isImplicit() &&
5771+
"default decls can only be added to the implicit/default constant "
5772+
"buffer $Globals");
5773+
DefaultBufferDecls.push_back(D);
5774+
}
5775+
5776+
HLSLBufferDecl::buffer_decl_iterator
5777+
HLSLBufferDecl::buffer_decls_begin() const {
5778+
return buffer_decl_iterator(llvm::iterator_range(DefaultBufferDecls.begin(),
5779+
DefaultBufferDecls.end()),
5780+
decl_range(decls_begin(), decls_end()));
5781+
}
5782+
5783+
HLSLBufferDecl::buffer_decl_iterator HLSLBufferDecl::buffer_decls_end() const {
5784+
return buffer_decl_iterator(
5785+
llvm::iterator_range(DefaultBufferDecls.end(), DefaultBufferDecls.end()),
5786+
decl_range(decls_end(), decls_end()));
5787+
}
5788+
5789+
bool HLSLBufferDecl::buffer_decls_empty() {
5790+
return DefaultBufferDecls.empty() && decls_empty();
5791+
}
5792+
57565793
//===----------------------------------------------------------------------===//
57575794
// ImportDecl Implementation
57585795
//===----------------------------------------------------------------------===//

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
244244
size_t BufferSize = 0;
245245
bool UsePackoffset = BufDecl->hasPackoffset();
246246
const auto *ElemIt = LayoutStruct->element_begin();
247-
for (Decl *D : BufDecl->decls()) {
247+
for (Decl *D : BufDecl->buffer_decls()) {
248248
if (isa<CXXRecordDecl, EmptyDecl>(D))
249249
// Nothing to do for this declaration.
250250
continue;
@@ -286,10 +286,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
286286
.str()))) &&
287287
"layout type does not match the converted element type");
288288

289-
// there might be resources inside the used defined structs
290-
if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType())
291-
// FIXME: handle resources in cbuffer structs
292-
llvm_unreachable("resources in cbuffer are not supported yet");
289+
// FIXME: handle resources in cbuffer user-defined structs
293290

294291
// create global variable for the constant and to metadata list
295292
GlobalVariable *ElemGV =

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5503,6 +5503,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
55035503
if (getLangOpts().OpenCL && ASTTy->isSamplerT())
55045504
return;
55055505

5506+
// HLSL default buffer constants will be emitted during HLSLBufferDecl codegen
5507+
if (getLangOpts().HLSL &&
5508+
D->getType().getAddressSpace() == LangAS::hlsl_constant)
5509+
return;
5510+
55065511
// If this is OpenMP device, check if it is legal to emit this global
55075512
// normally.
55085513
if (LangOpts.OpenMPIsTargetDevice && OpenMPRuntime &&

clang/lib/Sema/Sema.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,8 +1416,7 @@ void Sema::ActOnEndOfTranslationUnit() {
14161416
}
14171417

14181418
if (LangOpts.HLSL)
1419-
HLSL().DiagnoseAvailabilityViolations(
1420-
getASTContext().getTranslationUnitDecl());
1419+
HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
14211420

14221421
// If there were errors, disable 'unused' warnings since they will mostly be
14231422
// noise. Don't warn for a use from a module: either we should warn on all

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//===----------------------------------------------------------------------===//
1010

1111
#include "clang/Sema/SemaHLSL.h"
12+
#include "clang/AST/ASTConsumer.h"
1213
#include "clang/AST/ASTContext.h"
1314
#include "clang/AST/Attr.h"
1415
#include "clang/AST/Attrs.inc"
@@ -147,7 +148,7 @@ bool ResourceBindings::hasBindingInfoForDecl(const VarDecl *VD) const {
147148
return DeclToBindingListIndex.contains(VD);
148149
}
149150

150-
SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
151+
SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), DefaultCBuffer(nullptr) {}
151152

152153
Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
153154
SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -219,7 +220,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
219220
// or on none.
220221
bool HasPackOffset = false;
221222
bool HasNonPackOffset = false;
222-
for (auto *Field : BufDecl->decls()) {
223+
for (auto *Field : BufDecl->buffer_decls()) {
223224
VarDecl *Var = dyn_cast<VarDecl>(Field);
224225
if (!Var)
225226
continue;
@@ -486,7 +487,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
486487
LS->setImplicit(true);
487488
LS->startDefinition();
488489

489-
for (Decl *D : BufDecl->decls()) {
490+
for (Decl *D : BufDecl->buffer_decls()) {
490491
VarDecl *VD = dyn_cast<VarDecl>(D);
491492
if (!VD || VD->getStorageClass() == SC_Static ||
492493
VD->getType().getAddressSpace() == LangAS::hlsl_groupshared)
@@ -1922,7 +1923,21 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
19221923

19231924
} // namespace
19241925

1925-
void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
1926+
void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
1927+
1928+
// process default CBuffer - create buffer layout struct and invoke codegenCGH
1929+
if (DefaultCBuffer) {
1930+
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
1931+
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
1932+
1933+
DeclGroupRef DG(DefaultCBuffer);
1934+
SemaRef.Consumer.HandleTopLevelDecl(DG);
1935+
}
1936+
1937+
diagnoseAvailabilityViolations(TU);
1938+
}
1939+
1940+
void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
19261941
// Skip running the diagnostics scan if the diagnostic mode is
19271942
// strict (-fhlsl-strict-availability) and the target shader stage is known
19281943
// because all relevant diagnostics were already emitted in the
@@ -2784,6 +2799,14 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
27842799
return Ty;
27852800
}
27862801

2802+
static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
2803+
QualType QT = VD->getType();
2804+
return VD->getDeclContext()->isTranslationUnit() &&
2805+
QT.getAddressSpace() == LangAS::Default &&
2806+
VD->getStorageClass() != SC_Static &&
2807+
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
2808+
}
2809+
27872810
void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
27882811
if (VD->hasGlobalStorage()) {
27892812
// make sure the declaration has a complete type
@@ -2795,7 +2818,21 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
27952818
return;
27962819
}
27972820

2798-
// find all resources on decl
2821+
// Global variables outside a cbuffer block that are not a resource, static,
2822+
// groupshared, or an empty array or struct belong to the default constant
2823+
// buffer $Globals
2824+
if (IsDefaultBufferConstantDecl(VD)) {
2825+
if (DefaultCBuffer == nullptr)
2826+
DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
2827+
SemaRef.getASTContext(), SemaRef.getCurLexicalContext());
2828+
// update address space to hlsl_constant
2829+
QualType NewTy = getASTContext().getAddrSpaceQualType(
2830+
VD->getType(), LangAS::hlsl_constant);
2831+
VD->setType(NewTy);
2832+
DefaultCBuffer->addDefaultBufferDecl(VD);
2833+
}
2834+
2835+
// find all resources bindings on decl
27992836
if (VD->getType()->isHLSLIntangibleType())
28002837
collectResourcesOnVarDecl(VD);
28012838

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s
2+
3+
struct EmptyStruct {
4+
};
5+
6+
struct S {
7+
RWBuffer<float> buf;
8+
EmptyStruct es;
9+
float ea[0];
10+
float b;
11+
};
12+
13+
// CHECK: VarDecl {{.*}} used a 'hlsl_constant float'
14+
float a;
15+
16+
// CHECK: VarDecl {{.*}} b 'RWBuffer<float>':'hlsl::RWBuffer<float>'
17+
RWBuffer<float> b;
18+
19+
// CHECK: VarDecl {{.*}} c 'EmptyStruct'
20+
EmptyStruct c;
21+
22+
// CHECK: VarDecl {{.*}} d 'float[0]'
23+
float d[0];
24+
25+
// CHECK: VarDecl {{.*}} e 'RWBuffer<float>[2]'
26+
RWBuffer<float> e[2];
27+
28+
// CHECK: VarDecl {{.*}} f 'groupshared float'
29+
groupshared float f;
30+
31+
// CHECK: VarDecl {{.*}} g 'hlsl_constant float'
32+
float g;
33+
34+
// CHECK: VarDecl {{.*}} h 'hlsl_constant S'
35+
S h;
36+
37+
// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals
38+
// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_$Globals definition
39+
// CHECK: PackedAttr
40+
// CHECK-NEXT: FieldDecl {{.*}} a 'float'
41+
// CHECK-NEXT: FieldDecl {{.*}} g 'float'
42+
// CHECK-NEXT: FieldDecl {{.*}} h '__cblayout_S'
43+
44+
// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_S definition
45+
// CHECK: PackedAttr {{.*}} Implicit
46+
// CHECK-NEXT: FieldDecl {{.*}} b 'float'
47+
48+
export float foo() {
49+
return a;
50+
}

0 commit comments

Comments
 (0)