[llvm-branch-commits] [clang] [HLSL] Implement default constant buffer `$Globals` (PR #125807)

Helena Kotas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Feb 4 22:12:04 PST 2025


https://github.com/hekota created https://github.com/llvm/llvm-project/pull/125807

All variable declarations in the global scope that are not resources, static or empty are implicitly added to implicit constant buffer `$Globals`.

Fixes #123801

Depends on #124886.

>From 42bb34f66f0030f55e1055c4ee0b362511b7f45b Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 4 Feb 2025 22:01:49 -0800
Subject: [PATCH] [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
---
 clang/include/clang/AST/Decl.h              | 22 +++++++
 clang/include/clang/Sema/SemaHLSL.h         |  7 ++-
 clang/lib/AST/Decl.cpp                      | 41 ++++++++++++-
 clang/lib/CodeGen/CGHLSLRuntime.cpp         |  7 +--
 clang/lib/CodeGen/CodeGenModule.cpp         |  5 ++
 clang/lib/Sema/Sema.cpp                     |  3 +-
 clang/lib/Sema/SemaHLSL.cpp                 | 47 +++++++++++++--
 clang/test/AST/HLSL/default_cbuffer.hlsl    | 50 ++++++++++++++++
 clang/test/CodeGenHLSL/basic_types.hlsl     | 64 ++++++++++-----------
 clang/test/CodeGenHLSL/default_cbuffer.hlsl | 43 ++++++++++++++
 10 files changed, 242 insertions(+), 47 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 05e56978977f2b1..f86ddaf89bd9cfa 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5038,6 +5038,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);
@@ -5047,6 +5052,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 {
@@ -5061,6 +5068,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
   bool hasPackoffset() const { return HasPackoffset; }
   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()); }
@@ -5072,6 +5080,20 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
     return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(DC));
   }
 
+  // Iterator for the buffer decls. Concatenates the list of decls parented
+  // by this HLSLBufferDecl with the list of default buffer decls.
+  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 f4cd11f423a84a0..b1cc856975532fa 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -103,13 +103,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,
@@ -159,11 +159,16 @@ class SemaHLSL : public SemaBase {
   // List of all resource bindings
   ResourceBindings Bindings;
 
+  // default constant buffer $Globals
+  HLSLBufferDecl *DefaultCBuffer;
+
 private:
   void collectResourcesOnVarDecl(VarDecl *D);
   void collectResourcesOnUserRecordDecl(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 4c5201d0ba2ee90..eede936577f0962 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"
@@ -5741,10 +5742,22 @@ 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,
-                                    SourceLocation(), SourceLocation());
+  return new (C, ID)
+      HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
+                     SourceLocation(), SourceLocation());
 }
 
 void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
@@ -5753,6 +5766,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 52247173b69901b..a24f5ac87866770 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -244,7 +244,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
   size_t BufferSize = 0;
   bool UsePackoffset = BufDecl->hasPackoffset();
   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;
@@ -286,10 +286,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
                                   .str()))) &&
            "layout type does not match the converted element type");
 
-    // there might be resources inside the used defined structs
-    if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType())
-      // FIXME: handle resources in cbuffer structs
-      llvm_unreachable("resources in cbuffer are not supported yet");
+    // FIXME: handle resources in cbuffer user-defined structs
 
     // create global variable for the constant and to metadata list
     GlobalVariable *ElemGV =
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index eb8d3ceeeba4c0d..65ec9c06b9ee5ac 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5503,6 +5503,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 abb46d3a84e74e2..d3c350b58da1a09 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1416,8 +1416,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 1537a887318920f..977185fcf9997b7 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,
@@ -219,7 +220,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;
@@ -486,7 +487,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)
@@ -1922,7 +1923,21 @@ 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
@@ -2784,6 +2799,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
@@ -2795,7 +2818,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())
       collectResourcesOnVarDecl(VD);
 
diff --git a/clang/test/AST/HLSL/default_cbuffer.hlsl b/clang/test/AST/HLSL/default_cbuffer.hlsl
new file mode 100644
index 000000000000000..9e0fce7cc53cfad
--- /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 d987af45a649fb5..362042654ea8c4d 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 000000000000000..7368997b51ac9ec
--- /dev/null
+++ b/clang/test/CodeGenHLSL/default_cbuffer.hlsl
@@ -0,0 +1,43 @@
+// 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: %"struct.__cblayout_$Globals" = type { float, float, %struct.__cblayout_S }
+// CHECK: %struct.__cblayout_S = type { float }
+
+// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer", %"struct.__cblayout_$Globals")
+// CHECK-DAG: @a = external addrspace(2) global float
+// CHECK-DAG: @g = external addrspace(2) global float
+// CHECK-DAG: @h = external addrspace(2) global %struct.__cblayout_S
+
+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.cblayouts = !{![[S_LAYOUT:.*]], ![[CB_LAYOUT:.*]]}
+// CHECK: !hlsl.cbs = !{![[CB:.*]]}
+
+// CHECK: ![[S_LAYOUT]] = !{!"struct.__cblayout_S", i32 4, i32 0}
+// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_$Globals", i32 20, i32 0, i32 4, i32 16}
+// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h}
\ No newline at end of file



More information about the llvm-branch-commits mailing list