[clang] [HLSL] Implement default constant buffer $Globals (2nd attempt) (PR #128589)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 12:01:36 PST 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/128589
>From 5bdff894dd52d2ad68c1e3cfb62f007d2bc647c2 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 20 Feb 2025 17:27:53 -0800
Subject: [PATCH 1/3] [HLSL] Implement default constant buffer `$Globals`
(#125807)
All variable declarations in the global scope that are not resources,
static or empty are implicitly added to implicit constant buffer
`$Globals`. They are created in `hlsl_constant` address space and
collected in an implicit `HLSLBufferDecl` node that is added to the AST
at the end of the translation unit. Codegen is the same as for explicit
constant buffers.
Fixes #123801
---
clang/include/clang/AST/Decl.h | 30 ++++++++++
clang/include/clang/Sema/SemaHLSL.h | 7 ++-
clang/lib/AST/Decl.cpp | 36 ++++++++++++
clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 +-
clang/lib/CodeGen/CodeGenModule.cpp | 5 ++
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaHLSL.cpp | 45 +++++++++++++--
clang/test/AST/HLSL/default_cbuffer.hlsl | 50 ++++++++++++++++
clang/test/CodeGenHLSL/basic_types.hlsl | 64 ++++++++++-----------
clang/test/CodeGenHLSL/default_cbuffer.hlsl | 39 +++++++++++++
10 files changed, 240 insertions(+), 41 deletions(-)
create mode 100644 clang/test/AST/HLSL/default_cbuffer.hlsl
create mode 100644 clang/test/CodeGenHLSL/default_cbuffer.hlsl
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 0f96bf0762ca4..86e6d1417ae79 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5045,6 +5045,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
// LayoutStruct - Layout struct for the buffer
CXXRecordDecl *LayoutStruct;
+ // For default (implicit) constant buffer, a lisf of references of global
+ // decls that belong to the buffer. The decls are already parented by the
+ // translation unit context.
+ SmallVector<Decl *> DefaultBufferDecls;
+
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
@@ -5054,6 +5059,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
+ static HLSLBufferDecl *CreateDefaultCBuffer(ASTContext &C,
+ DeclContext *LexicalParent);
static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
SourceRange getSourceRange() const override LLVM_READONLY {
@@ -5068,6 +5075,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
bool hasValidPackoffset() const { return HasValidPackoffset; }
const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; }
void addLayoutStruct(CXXRecordDecl *LS);
+ void addDefaultBufferDecl(Decl *D);
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
@@ -5079,6 +5087,28 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(DC));
}
+ // Iterator for the buffer decls. For constant buffers explicitly declared
+ // with `cbuffer` keyword this will the list of decls parented by this
+ // HLSLBufferDecl (equal to `decls()`).
+ // For implicit $Globals buffer this will be the list of default buffer
+ // declarations stored in DefaultBufferDecls plus the implicit layout
+ // struct (the only child of HLSLBufferDecl in this case).
+ //
+ // The iterator uses llvm::concat_iterator to concatenate the lists
+ // `decls()` and `DefaultBufferDecls`. For non-default buffers
+ // `DefaultBufferDecls` is always empty.
+ using buffer_decl_iterator =
+ llvm::concat_iterator<Decl *const, SmallVector<Decl *>::const_iterator,
+ decl_iterator>;
+ using buffer_decl_range = llvm::iterator_range<buffer_decl_iterator>;
+
+ buffer_decl_range buffer_decls() const {
+ return buffer_decl_range(buffer_decls_begin(), buffer_decls_end());
+ }
+ buffer_decl_iterator buffer_decls_begin() const;
+ buffer_decl_iterator buffer_decls_end() const;
+ bool buffer_decls_empty();
+
friend class ASTDeclReader;
friend class ASTDeclWriter;
};
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 4f4bbe95476ee..c4006fd13da17 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -105,13 +105,13 @@ class SemaHLSL : public SemaBase {
HLSLParamModifierAttr::Spelling Spelling);
void ActOnTopLevelFunction(FunctionDecl *FD);
void ActOnVariableDeclarator(VarDecl *VD);
+ void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
void CheckEntryPoint(FunctionDecl *FD);
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
const HLSLAnnotationAttr *AnnotationAttr);
void DiagnoseAttrStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
- void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
QualType LHSType, QualType RHSType,
@@ -168,11 +168,16 @@ class SemaHLSL : public SemaBase {
// List of all resource bindings
ResourceBindings Bindings;
+ // default constant buffer $Globals
+ HLSLBufferDecl *DefaultCBuffer;
+
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD,
const RecordType *RT);
void processExplicitBindingsOnDecl(VarDecl *D);
+
+ void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
};
} // namespace clang
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 5a3be1690f335..9c0be5ce04542 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -57,6 +57,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
@@ -5745,6 +5746,17 @@ HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
return Result;
}
+HLSLBufferDecl *
+HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C,
+ DeclContext *LexicalParent) {
+ DeclContext *DC = LexicalParent;
+ IdentifierInfo *II = &C.Idents.get("$Globals", tok::TokenKind::identifier);
+ HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl(
+ DC, true, SourceLocation(), II, SourceLocation(), SourceLocation());
+ Result->setImplicit(true);
+ return Result;
+}
+
HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
@@ -5757,6 +5769,30 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
addDecl(LS);
}
+void HLSLBufferDecl::addDefaultBufferDecl(Decl *D) {
+ assert(isImplicit() &&
+ "default decls can only be added to the implicit/default constant "
+ "buffer $Globals");
+ DefaultBufferDecls.push_back(D);
+}
+
+HLSLBufferDecl::buffer_decl_iterator
+HLSLBufferDecl::buffer_decls_begin() const {
+ return buffer_decl_iterator(llvm::iterator_range(DefaultBufferDecls.begin(),
+ DefaultBufferDecls.end()),
+ decl_range(decls_begin(), decls_end()));
+}
+
+HLSLBufferDecl::buffer_decl_iterator HLSLBufferDecl::buffer_decls_end() const {
+ return buffer_decl_iterator(
+ llvm::iterator_range(DefaultBufferDecls.end(), DefaultBufferDecls.end()),
+ decl_range(decls_end(), decls_end()));
+}
+
+bool HLSLBufferDecl::buffer_decls_empty() {
+ return DefaultBufferDecls.empty() && decls_empty();
+}
+
//===----------------------------------------------------------------------===//
// ImportDecl Implementation
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 547220fb1f1e1..ed6d2036cb984 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -116,7 +116,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
BufGlobals.push_back(ValueAsMetadata::get(BufGV));
const auto *ElemIt = LayoutStruct->element_begin();
- for (Decl *D : BufDecl->decls()) {
+ for (Decl *D : BufDecl->buffer_decls()) {
if (isa<CXXRecordDecl, EmptyDecl>(D))
// Nothing to do for this declaration.
continue;
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 7924c32fcf633..1b7d0ac89690e 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5513,6 +5513,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
if (getLangOpts().OpenCL && ASTTy->isSamplerT())
return;
+ // HLSL default buffer constants will be emitted during HLSLBufferDecl codegen
+ if (getLangOpts().HLSL &&
+ D->getType().getAddressSpace() == LangAS::hlsl_constant)
+ return;
+
// If this is OpenMP device, check if it is legal to emit this global
// normally.
if (LangOpts.OpenMPIsTargetDevice && OpenMPRuntime &&
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 145cda6c46b9b..c699e92985156 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1417,8 +1417,7 @@ void Sema::ActOnEndOfTranslationUnit() {
}
if (LangOpts.HLSL)
- HLSL().DiagnoseAvailabilityViolations(
- getASTContext().getTranslationUnitDecl());
+ HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
// If there were errors, disable 'unused' warnings since they will mostly be
// noise. Don't warn for a use from a module: either we should warn on all
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d26d85d5861b1..b2984234d6171 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -9,6 +9,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaHLSL.h"
+#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
@@ -147,7 +148,7 @@ bool ResourceBindings::hasBindingInfoForDecl(const VarDecl *VD) const {
return DeclToBindingListIndex.contains(VD);
}
-SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
+SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), DefaultCBuffer(nullptr) {}
Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -225,7 +226,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
// or on none.
bool HasPackOffset = false;
bool HasNonPackOffset = false;
- for (auto *Field : BufDecl->decls()) {
+ for (auto *Field : BufDecl->buffer_decls()) {
VarDecl *Var = dyn_cast<VarDecl>(Field);
if (!Var)
continue;
@@ -492,7 +493,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
LS->setImplicit(true);
LS->startDefinition();
- for (Decl *D : BufDecl->decls()) {
+ for (Decl *D : BufDecl->buffer_decls()) {
VarDecl *VD = dyn_cast<VarDecl>(D);
if (!VD || VD->getStorageClass() == SC_Static ||
VD->getType().getAddressSpace() == LangAS::hlsl_groupshared)
@@ -1928,7 +1929,19 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
} // namespace
-void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
+void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
+ // process default CBuffer - create buffer layout struct and invoke codegenCGH
+ if (DefaultCBuffer) {
+ SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
+ createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
+
+ DeclGroupRef DG(DefaultCBuffer);
+ SemaRef.Consumer.HandleTopLevelDecl(DG);
+ }
+ diagnoseAvailabilityViolations(TU);
+}
+
+void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
// Skip running the diagnostics scan if the diagnostic mode is
// strict (-fhlsl-strict-availability) and the target shader stage is known
// because all relevant diagnostics were already emitted in the
@@ -2991,6 +3004,14 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
+static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+ QualType QT = VD->getType();
+ return VD->getDeclContext()->isTranslationUnit() &&
+ QT.getAddressSpace() == LangAS::Default &&
+ VD->getStorageClass() != SC_Static &&
+ !isInvalidConstantBufferLeafElementType(QT.getTypePtr());
+}
+
void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
if (VD->hasGlobalStorage()) {
// make sure the declaration has a complete type
@@ -3002,7 +3023,21 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
return;
}
- // find all resources on decl
+ // Global variables outside a cbuffer block that are not a resource, static,
+ // groupshared, or an empty array or struct belong to the default constant
+ // buffer $Globals
+ if (IsDefaultBufferConstantDecl(VD)) {
+ if (DefaultCBuffer == nullptr)
+ DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
+ SemaRef.getASTContext(), SemaRef.getCurLexicalContext());
+ // update address space to hlsl_constant
+ QualType NewTy = getASTContext().getAddrSpaceQualType(
+ VD->getType(), LangAS::hlsl_constant);
+ VD->setType(NewTy);
+ DefaultCBuffer->addDefaultBufferDecl(VD);
+ }
+
+ // find all resources bindings on decl
if (VD->getType()->isHLSLIntangibleType())
collectResourceBindingsOnVarDecl(VD);
diff --git a/clang/test/AST/HLSL/default_cbuffer.hlsl b/clang/test/AST/HLSL/default_cbuffer.hlsl
new file mode 100644
index 0000000000000..9e0fce7cc53cf
--- /dev/null
+++ b/clang/test/AST/HLSL/default_cbuffer.hlsl
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s
+
+struct EmptyStruct {
+};
+
+struct S {
+ RWBuffer<float> buf;
+ EmptyStruct es;
+ float ea[0];
+ float b;
+};
+
+// CHECK: VarDecl {{.*}} used a 'hlsl_constant float'
+float a;
+
+// CHECK: VarDecl {{.*}} b 'RWBuffer<float>':'hlsl::RWBuffer<float>'
+RWBuffer<float> b;
+
+// CHECK: VarDecl {{.*}} c 'EmptyStruct'
+EmptyStruct c;
+
+// CHECK: VarDecl {{.*}} d 'float[0]'
+float d[0];
+
+// CHECK: VarDecl {{.*}} e 'RWBuffer<float>[2]'
+RWBuffer<float> e[2];
+
+// CHECK: VarDecl {{.*}} f 'groupshared float'
+groupshared float f;
+
+// CHECK: VarDecl {{.*}} g 'hlsl_constant float'
+float g;
+
+// CHECK: VarDecl {{.*}} h 'hlsl_constant S'
+S h;
+
+// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals
+// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_$Globals definition
+// CHECK: PackedAttr
+// CHECK-NEXT: FieldDecl {{.*}} a 'float'
+// CHECK-NEXT: FieldDecl {{.*}} g 'float'
+// CHECK-NEXT: FieldDecl {{.*}} h '__cblayout_S'
+
+// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_S definition
+// CHECK: PackedAttr {{.*}} Implicit
+// CHECK-NEXT: FieldDecl {{.*}} b 'float'
+
+export float foo() {
+ return a;
+}
diff --git a/clang/test/CodeGenHLSL/basic_types.hlsl b/clang/test/CodeGenHLSL/basic_types.hlsl
index d987af45a649f..362042654ea8c 100644
--- a/clang/test/CodeGenHLSL/basic_types.hlsl
+++ b/clang/test/CodeGenHLSL/basic_types.hlsl
@@ -6,38 +6,38 @@
// RUN: -emit-llvm -disable-llvm-passes -o - -DNAMESPACED| FileCheck %s
-// CHECK: @uint16_t_Val = global i16 0, align 2
-// CHECK: @int16_t_Val = global i16 0, align 2
-// CHECK: @uint_Val = global i32 0, align 4
-// CHECK: @uint64_t_Val = global i64 0, align 8
-// CHECK: @int64_t_Val = global i64 0, align 8
-// CHECK: @int16_t2_Val = global <2 x i16> zeroinitializer, align 4
-// CHECK: @int16_t3_Val = global <3 x i16> zeroinitializer, align 8
-// CHECK: @int16_t4_Val = global <4 x i16> zeroinitializer, align 8
-// CHECK: @uint16_t2_Val = global <2 x i16> zeroinitializer, align 4
-// CHECK: @uint16_t3_Val = global <3 x i16> zeroinitializer, align 8
-// CHECK: @uint16_t4_Val = global <4 x i16> zeroinitializer, align 8
-// CHECK: @int2_Val = global <2 x i32> zeroinitializer, align 8
-// CHECK: @int3_Val = global <3 x i32> zeroinitializer, align 16
-// CHECK: @int4_Val = global <4 x i32> zeroinitializer, align 16
-// CHECK: @uint2_Val = global <2 x i32> zeroinitializer, align 8
-// CHECK: @uint3_Val = global <3 x i32> zeroinitializer, align 16
-// CHECK: @uint4_Val = global <4 x i32> zeroinitializer, align 16
-// CHECK: @int64_t2_Val = global <2 x i64> zeroinitializer, align 16
-// CHECK: @int64_t3_Val = global <3 x i64> zeroinitializer, align 32
-// CHECK: @int64_t4_Val = global <4 x i64> zeroinitializer, align 32
-// CHECK: @uint64_t2_Val = global <2 x i64> zeroinitializer, align 16
-// CHECK: @uint64_t3_Val = global <3 x i64> zeroinitializer, align 32
-// CHECK: @uint64_t4_Val = global <4 x i64> zeroinitializer, align 32
-// CHECK: @half2_Val = global <2 x half> zeroinitializer, align 4
-// CHECK: @half3_Val = global <3 x half> zeroinitializer, align 8
-// CHECK: @half4_Val = global <4 x half> zeroinitializer, align 8
-// CHECK: @float2_Val = global <2 x float> zeroinitializer, align 8
-// CHECK: @float3_Val = global <3 x float> zeroinitializer, align 16
-// CHECK: @float4_Val = global <4 x float> zeroinitializer, align 16
-// CHECK: @double2_Val = global <2 x double> zeroinitializer, align 16
-// CHECK: @double3_Val = global <3 x double> zeroinitializer, align 32
-// CHECK: @double4_Val = global <4 x double> zeroinitializer, align 32
+// CHECK: @uint16_t_Val = external addrspace(2) global i16, align 2
+// CHECK: @int16_t_Val = external addrspace(2) global i16, align 2
+// CHECK: @uint_Val = external addrspace(2) global i32, align 4
+// CHECK: @uint64_t_Val = external addrspace(2) global i64, align 8
+// CHECK: @int64_t_Val = external addrspace(2) global i64, align 8
+// CHECK: @int16_t2_Val = external addrspace(2) global <2 x i16>, align 4
+// CHECK: @int16_t3_Val = external addrspace(2) global <3 x i16>, align 8
+// CHECK: @int16_t4_Val = external addrspace(2) global <4 x i16>, align 8
+// CHECK: @uint16_t2_Val = external addrspace(2) global <2 x i16>, align 4
+// CHECK: @uint16_t3_Val = external addrspace(2) global <3 x i16>, align 8
+// CHECK: @uint16_t4_Val = external addrspace(2) global <4 x i16>, align 8
+// CHECK: @int2_Val = external addrspace(2) global <2 x i32>, align 8
+// CHECK: @int3_Val = external addrspace(2) global <3 x i32>, align 16
+// CHECK: @int4_Val = external addrspace(2) global <4 x i32>, align 16
+// CHECK: @uint2_Val = external addrspace(2) global <2 x i32>, align 8
+// CHECK: @uint3_Val = external addrspace(2) global <3 x i32>, align 16
+// CHECK: @uint4_Val = external addrspace(2) global <4 x i32>, align 16
+// CHECK: @int64_t2_Val = external addrspace(2) global <2 x i64>, align 16
+// CHECK: @int64_t3_Val = external addrspace(2) global <3 x i64>, align 32
+// CHECK: @int64_t4_Val = external addrspace(2) global <4 x i64>, align 32
+// CHECK: @uint64_t2_Val = external addrspace(2) global <2 x i64>, align 16
+// CHECK: @uint64_t3_Val = external addrspace(2) global <3 x i64>, align 32
+// CHECK: @uint64_t4_Val = external addrspace(2) global <4 x i64>, align 32
+// CHECK: @half2_Val = external addrspace(2) global <2 x half>, align 4
+// CHECK: @half3_Val = external addrspace(2) global <3 x half>, align 8
+// CHECK: @half4_Val = external addrspace(2) global <4 x half>, align 8
+// CHECK: @float2_Val = external addrspace(2) global <2 x float>, align 8
+// CHECK: @float3_Val = external addrspace(2) global <3 x float>, align 16
+// CHECK: @float4_Val = external addrspace(2) global <4 x float>, align 16
+// CHECK: @double2_Val = external addrspace(2) global <2 x double>, align 16
+// CHECK: @double3_Val = external addrspace(2) global <3 x double>, align 32
+// CHECK: @double4_Val = external addrspace(2) global <4 x double>, align 32
#ifdef NAMESPACED
#define TYPE_DECL(T) hlsl::T T##_Val
diff --git a/clang/test/CodeGenHLSL/default_cbuffer.hlsl b/clang/test/CodeGenHLSL/default_cbuffer.hlsl
new file mode 100644
index 0000000000000..c5176aa8466e4
--- /dev/null
+++ b/clang/test/CodeGenHLSL/default_cbuffer.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \
+// RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+
+// CHECK: %"__cblayout_$Globals" = type <{ float, float, target("dx.Layout", %__cblayout_S, 4, 0) }>
+// CHECK: %__cblayout_S = type <{ float }>
+
+// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer", target("dx.Layout", %"__cblayout_$Globals", 20, 0, 4, 16))
+// CHECK-DAG: @a = external addrspace(2) global float
+// CHECK-DAG: @g = external addrspace(2) global float
+// CHECK-DAG: @h = external addrspace(2) global target("dx.Layout", %__cblayout_S, 4, 0), align 4
+
+struct EmptyStruct {
+};
+
+struct S {
+ RWBuffer<float> buf;
+ EmptyStruct es;
+ float ea[0];
+ float b;
+};
+
+float a;
+RWBuffer<float> b;
+EmptyStruct c;
+float d[0];
+RWBuffer<float> e[2];
+groupshared float f;
+float g;
+S h;
+
+RWBuffer<float> Buf;
+
+[numthreads(4,1,1)]
+void main() {
+ Buf[0] = a;
+}
+
+// CHECK: !hlsl.cbs = !{![[CB:.*]]}
+// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h}
>From ded2c28f336121d70b84bb518fe89f74cf0e0ed2 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 24 Feb 2025 14:30:08 -0800
Subject: [PATCH 2/3] Allocate list of default constant decls with ASTContext
---
clang/include/clang/AST/Decl.h | 15 +++++++++------
clang/include/clang/Sema/SemaHLSL.h | 5 +++--
clang/lib/AST/Decl.cpp | 15 +++++++++++----
clang/lib/Sema/SemaHLSL.cpp | 14 +++++++-------
4 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 86e6d1417ae79..efac36e49351e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5045,22 +5045,26 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
// LayoutStruct - Layout struct for the buffer
CXXRecordDecl *LayoutStruct;
- // For default (implicit) constant buffer, a lisf of references of global
+ // For default (implicit) constant buffer, an array of references of global
// decls that belong to the buffer. The decls are already parented by the
- // translation unit context.
- SmallVector<Decl *> DefaultBufferDecls;
+ // translation unit context. The array is allocated by the ASTContext
+ // allocator in HLSLBufferDecl::CreateDefaultCBuffer.
+ ArrayRef<Decl *> DefaultBufferDecls;
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
+ void setDefaultBufferDecls(ArrayRef<Decl *> Decls);
+
public:
static HLSLBufferDecl *Create(ASTContext &C, DeclContext *LexicalParent,
bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
- static HLSLBufferDecl *CreateDefaultCBuffer(ASTContext &C,
- DeclContext *LexicalParent);
+ static HLSLBufferDecl *
+ CreateDefaultCBuffer(ASTContext &C, DeclContext *LexicalParent,
+ ArrayRef<Decl *> DefaultCBufferDecls);
static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
SourceRange getSourceRange() const override LLVM_READONLY {
@@ -5075,7 +5079,6 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
bool hasValidPackoffset() const { return HasValidPackoffset; }
const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; }
void addLayoutStruct(CXXRecordDecl *LS);
- void addDefaultBufferDecl(Decl *D);
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index c4006fd13da17..f333fe30e8da0 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -168,8 +168,9 @@ class SemaHLSL : public SemaBase {
// List of all resource bindings
ResourceBindings Bindings;
- // default constant buffer $Globals
- HLSLBufferDecl *DefaultCBuffer;
+ // Global declaration collected for the $Globals default constant
+ // buffer which will be created at the end of the translation unit.
+ llvm::SmallVector<Decl *> DefaultCBufferDecls;
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 9c0be5ce04542..b423ff76be0fb 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5747,13 +5747,20 @@ HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
}
HLSLBufferDecl *
-HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C,
- DeclContext *LexicalParent) {
+HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C, DeclContext *LexicalParent,
+ ArrayRef<Decl *> DefaultCBufferDecls) {
DeclContext *DC = LexicalParent;
IdentifierInfo *II = &C.Idents.get("$Globals", tok::TokenKind::identifier);
HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl(
DC, true, SourceLocation(), II, SourceLocation(), SourceLocation());
Result->setImplicit(true);
+
+ // allocate array for default decls with ASTContext allocator
+ assert(!DefaultCBufferDecls.empty());
+ Decl **DeclsArray = new (C) Decl *[DefaultCBufferDecls.size()];
+ std::copy(DefaultCBufferDecls.begin(), DefaultCBufferDecls.end(), DeclsArray);
+ Result->setDefaultBufferDecls(
+ ArrayRef<Decl *>(DeclsArray, DefaultCBufferDecls.size()));
return Result;
}
@@ -5769,11 +5776,11 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
addDecl(LS);
}
-void HLSLBufferDecl::addDefaultBufferDecl(Decl *D) {
+void HLSLBufferDecl::setDefaultBufferDecls(ArrayRef<Decl *> Decls) {
assert(isImplicit() &&
"default decls can only be added to the implicit/default constant "
"buffer $Globals");
- DefaultBufferDecls.push_back(D);
+ DefaultBufferDecls = Decls;
}
HLSLBufferDecl::buffer_decl_iterator
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index b2984234d6171..67a719ca969df 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -148,7 +148,7 @@ bool ResourceBindings::hasBindingInfoForDecl(const VarDecl *VD) const {
return DeclToBindingListIndex.contains(VD);
}
-SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), DefaultCBuffer(nullptr) {}
+SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -1931,7 +1931,10 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
// process default CBuffer - create buffer layout struct and invoke codegenCGH
- if (DefaultCBuffer) {
+ if (!DefaultCBufferDecls.empty()) {
+ HLSLBufferDecl *DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
+ SemaRef.getASTContext(), SemaRef.getCurLexicalContext(),
+ DefaultCBufferDecls);
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
@@ -3025,16 +3028,13 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
- // buffer $Globals
+ // buffer $Globals (to be created at the end of the translation unit).
if (IsDefaultBufferConstantDecl(VD)) {
- if (DefaultCBuffer == nullptr)
- DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
- SemaRef.getASTContext(), SemaRef.getCurLexicalContext());
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
VD->setType(NewTy);
- DefaultCBuffer->addDefaultBufferDecl(VD);
+ DefaultCBufferDecls.push_back(VD);
}
// find all resources bindings on decl
>From e21e3596b6b67f73f86c642e367aa23f709199e8 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 25 Feb 2025 12:00:36 -0800
Subject: [PATCH 3/3] code review feedback - allocate default decl array in
setDefaultBufferDecls
---
clang/lib/AST/Decl.cpp | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b423ff76be0fb..e8aeacf24374f 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5754,13 +5754,7 @@ HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C, DeclContext *LexicalParent,
HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl(
DC, true, SourceLocation(), II, SourceLocation(), SourceLocation());
Result->setImplicit(true);
-
- // allocate array for default decls with ASTContext allocator
- assert(!DefaultCBufferDecls.empty());
- Decl **DeclsArray = new (C) Decl *[DefaultCBufferDecls.size()];
- std::copy(DefaultCBufferDecls.begin(), DefaultCBufferDecls.end(), DeclsArray);
- Result->setDefaultBufferDecls(
- ArrayRef<Decl *>(DeclsArray, DefaultCBufferDecls.size()));
+ Result->setDefaultBufferDecls(DefaultCBufferDecls);
return Result;
}
@@ -5777,10 +5771,16 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
}
void HLSLBufferDecl::setDefaultBufferDecls(ArrayRef<Decl *> Decls) {
+ assert(!Decls.empty());
+ assert(DefaultBufferDecls.empty() && "default decls are already set");
assert(isImplicit() &&
"default decls can only be added to the implicit/default constant "
"buffer $Globals");
- DefaultBufferDecls = Decls;
+
+ // allocate array for default decls with ASTContext allocator
+ Decl **DeclsArray = new (getASTContext()) Decl *[Decls.size()];
+ std::copy(Decls.begin(), Decls.end(), DeclsArray);
+ DefaultBufferDecls = ArrayRef<Decl *>(DeclsArray, Decls.size());
}
HLSLBufferDecl::buffer_decl_iterator
More information about the cfe-commits
mailing list