[clang] 19af858 - [HLSL] Constant Buffers CodeGen (#124886)

via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 20 10:32:18 PST 2025


Author: Helena Kotas
Date: 2025-02-20T10:32:14-08:00
New Revision: 19af8581d51b8144f6d041ae1d948443084d8d0b

URL: https://github.com/llvm/llvm-project/commit/19af8581d51b8144f6d041ae1d948443084d8d0b
DIFF: https://github.com/llvm/llvm-project/commit/19af8581d51b8144f6d041ae1d948443084d8d0b.diff

LOG: [HLSL] Constant Buffers CodeGen (#124886)

Translates `cbuffer` declaration blocks to `target("dx.CBuffer")` type. Creates global variables in `hlsl_constant` address space for all `cbuffer` constant and adds metadata describing which global constant belongs to which constant buffer. For explicit constant buffer layout information an explicit layout type `target("dx.Layout")` is used. This might change in the future.

The constant globals are temporary and will be removed in upcoming pass that will translate `load` instructions in the `hlsl_constant` address space to constant buffer load intrinsics calls off a CBV handle (#124630, #112992).

See [Constant buffer design
doc](https://github.com/llvm/wg-hlsl/pull/94) for more details.

Fixes #113514, #106596

Added: 
    clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
    clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
    clang/test/CodeGenHLSL/cbuffer.hlsl
    clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
    clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
    clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl

Modified: 
    clang/include/clang/AST/Decl.h
    clang/include/clang/AST/Type.h
    clang/lib/AST/Decl.cpp
    clang/lib/CodeGen/CGHLSLRuntime.cpp
    clang/lib/CodeGen/CGHLSLRuntime.h
    clang/lib/CodeGen/CMakeLists.txt
    clang/lib/CodeGen/TargetInfo.h
    clang/lib/CodeGen/Targets/DirectX.cpp
    clang/lib/CodeGen/Targets/SPIR.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaHLSL.cpp

Removed: 
    clang/test/CodeGenHLSL/cbuf.hlsl
    clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
    clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl


################################################################################
diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index f305cbbce4c60..0f96bf0762ca4 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5039,6 +5039,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
   SourceLocation KwLoc;
   /// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer).
   bool IsCBuffer;
+  /// HasValidPackoffset - Whether the buffer has valid packoffset annotations
+  //                       on all declarations
+  bool HasValidPackoffset;
+  // LayoutStruct - Layout struct for the buffer
+  CXXRecordDecl *LayoutStruct;
 
   HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
                  IdentifierInfo *ID, SourceLocation IDLoc,
@@ -5059,6 +5064,10 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
   void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
   bool isCBuffer() const { return IsCBuffer; }
+  void setHasValidPackoffset(bool PO) { HasValidPackoffset = PO; }
+  bool hasValidPackoffset() const { return HasValidPackoffset; }
+  const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; }
+  void addLayoutStruct(CXXRecordDecl *LS);
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }

diff  --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 1d9743520654e..c3ff7ebd88516 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -6266,8 +6266,8 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
     LLVM_PREFERRED_TYPE(bool)
     uint8_t RawBuffer : 1;
 
-    Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV,
-               bool RawBuffer)
+    Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false,
+               bool RawBuffer = false)
         : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {}
 
     Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {}

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 610207cf8b9a4..5a3be1690f335 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1747,6 +1747,10 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
       }
     }
 
+    // Suppress transparent contexts like export or HLSLBufferDecl context
+    if (Ctx->isTransparentContext())
+      continue;
+
     // Skip non-named contexts such as linkage specifications and ExportDecls.
     const NamedDecl *ND = dyn_cast<NamedDecl>(Ctx);
     if (!ND)
@@ -5717,7 +5721,7 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer,
                                SourceLocation IDLoc, SourceLocation LBrace)
     : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)),
       DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc),
-      IsCBuffer(CBuffer) {}
+      IsCBuffer(CBuffer), HasValidPackoffset(false), LayoutStruct(nullptr) {}
 
 HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
                                        DeclContext *LexicalParent, bool CBuffer,
@@ -5747,6 +5751,12 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
                                     SourceLocation(), SourceLocation());
 }
 
+void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
+  assert(LayoutStruct == nullptr && "layout struct has already been set");
+  LayoutStruct = LS;
+  addDecl(LS);
+}
+
 //===----------------------------------------------------------------------===//
 // ImportDecl Implementation
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 856d8b1b2948d..547220fb1f1e1 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -17,16 +17,22 @@
 #include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/TargetOptions.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
 #include "llvm/IR/Value.h"
 #include "llvm/Support/Alignment.h"
 
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FormatVariadic.h"
 
 using namespace clang;
@@ -34,6 +40,9 @@ using namespace CodeGen;
 using namespace clang::hlsl;
 using namespace llvm;
 
+static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
+                                 unsigned Slot, unsigned Space);
+
 namespace {
 
 void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
@@ -56,58 +65,17 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
   auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey);
   DXILValMD->addOperand(Val);
 }
-// cbuffer will be translated into global variable in special address space.
-// If translate into C,
-// cbuffer A {
-//   float a;
-//   float b;
-// }
-// float foo() { return a + b; }
-//
-// will be translated into
-//
-// struct A {
-//   float a;
-//   float b;
-// } cbuffer_A __attribute__((address_space(4)));
-// float foo() { return cbuffer_A.a + cbuffer_A.b; }
-//
-// layoutBuffer will create the struct A type.
-// replaceBuffer will replace use of global variable a and b with cbuffer_A.a
-// and cbuffer_A.b.
-//
-void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) {
-  if (Buf.Constants.empty())
-    return;
-
-  std::vector<llvm::Type *> EltTys;
-  for (auto &Const : Buf.Constants) {
-    GlobalVariable *GV = Const.first;
-    Const.second = EltTys.size();
-    llvm::Type *Ty = GV->getValueType();
-    EltTys.emplace_back(Ty);
-  }
-  Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys);
-}
-
-GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) {
-  // Create global variable for CB.
-  GlobalVariable *CBGV = new GlobalVariable(
-      Buf.LayoutStruct, /*isConstant*/ true,
-      GlobalValue::LinkageTypes::ExternalLinkage, nullptr,
-      llvm::formatv("{0}{1}", Buf.Name, Buf.IsCBuffer ? ".cb." : ".tb."),
-      GlobalValue::NotThreadLocal);
-
-  return CBGV;
-}
 
 } // namespace
 
-llvm::Type *CGHLSLRuntime::convertHLSLSpecificType(const Type *T) {
+llvm::Type *
+CGHLSLRuntime::convertHLSLSpecificType(const Type *T,
+                                       SmallVector<unsigned> *Packoffsets) {
   assert(T->isHLSLSpecificType() && "Not an HLSL specific type!");
 
   // Check if the target has a specific translation for this type first.
-  if (llvm::Type *TargetTy = CGM.getTargetCodeGenInfo().getHLSLType(CGM, T))
+  if (llvm::Type *TargetTy =
+          CGM.getTargetCodeGenInfo().getHLSLType(CGM, T, Packoffsets))
     return TargetTy;
 
   llvm_unreachable("Generic handling of HLSL types is not supported.");
@@ -117,48 +85,174 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() {
   return CGM.getTarget().getTriple().getArch();
 }
 
-void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
-  if (D->getStorageClass() == SC_Static) {
-    // For static inside cbuffer, take as global static.
-    // Don't add to cbuffer.
-    CGM.EmitGlobal(D);
-    return;
-  }
+// Returns true if the type is an HLSL resource class
+static bool isResourceRecordType(const clang::Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
 
-  auto *GV = cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(D));
-  GV->setExternallyInitialized(true);
-  // Add debug info for constVal.
-  if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
-    if (CGM.getCodeGenOpts().getDebugInfo() >=
-        codegenoptions::DebugInfoKind::LimitedDebugInfo)
-      DI->EmitGlobalVariable(cast<GlobalVariable>(GV), D);
-
-  // FIXME: support packoffset.
-  // See https://github.com/llvm/llvm-project/issues/57914.
-  uint32_t Offset = 0;
-  bool HasUserOffset = false;
-
-  unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX;
-  CB.Constants.emplace_back(std::make_pair(GV, LowerBound));
+// Returns true if the type is an HLSL resource class or an array of them
+static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) {
+  while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
+    Ty = CAT->getArrayElementTypeNoTypeQual();
+  return isResourceRecordType(Ty);
 }
 
-void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) {
-  for (Decl *it : DC->decls()) {
-    if (auto *ConstDecl = dyn_cast<VarDecl>(it)) {
-      addConstant(ConstDecl, CB);
-    } else if (isa<CXXRecordDecl, EmptyDecl>(it)) {
+// Emits constant global variables for buffer constants declarations
+// and creates metadata linking the constant globals with the buffer global.
+void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
+                                                 llvm::GlobalVariable *BufGV) {
+  LLVMContext &Ctx = CGM.getLLVMContext();
+
+  // get the layout struct from constant buffer target type
+  llvm::Type *BufType = BufGV->getValueType();
+  llvm::Type *BufLayoutType =
+      cast<llvm::TargetExtType>(BufType)->getTypeParameter(0);
+  llvm::StructType *LayoutStruct = cast<llvm::StructType>(
+      cast<llvm::TargetExtType>(BufLayoutType)->getTypeParameter(0));
+
+  // Start metadata list associating the buffer global variable with its
+  // constatns
+  SmallVector<llvm::Metadata *> BufGlobals;
+  BufGlobals.push_back(ValueAsMetadata::get(BufGV));
+
+  const auto *ElemIt = LayoutStruct->element_begin();
+  for (Decl *D : BufDecl->decls()) {
+    if (isa<CXXRecordDecl, EmptyDecl>(D))
       // Nothing to do for this declaration.
-    } else if (isa<FunctionDecl>(it)) {
-      // A function within an cbuffer is effectively a top-level function,
-      // as it only refers to globally scoped declarations.
-      CGM.EmitTopLevelDecl(it);
+      continue;
+    if (isa<FunctionDecl>(D)) {
+      // A function within an cbuffer is effectively a top-level function.
+      CGM.EmitTopLevelDecl(D);
+      continue;
     }
+    VarDecl *VD = dyn_cast<VarDecl>(D);
+    if (!VD)
+      continue;
+
+    QualType VDTy = VD->getType();
+    if (VDTy.getAddressSpace() != LangAS::hlsl_constant) {
+      if (VD->getStorageClass() == SC_Static ||
+          VDTy.getAddressSpace() == LangAS::hlsl_groupshared ||
+          isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) {
+        // Emit static and groupshared variables and resource classes inside
+        // cbuffer as regular globals
+        CGM.EmitGlobal(VD);
+      } else {
+        // Anything else that is not in the hlsl_constant address space must be
+        // an empty struct or a zero-sized array and can be ignored
+        assert(BufDecl->getASTContext().getTypeSize(VDTy) == 0 &&
+               "constant buffer decl with non-zero sized type outside of "
+               "hlsl_constant address space");
+      }
+      continue;
+    }
+
+    assert(ElemIt != LayoutStruct->element_end() &&
+           "number of elements in layout struct does not match");
+    llvm::Type *LayoutType = *ElemIt++;
+
+    // FIXME: handle resources inside user defined structs
+    // (llvm/wg-hlsl#175)
+
+    // create global variable for the constant and to metadata list
+    GlobalVariable *ElemGV =
+        cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType));
+    BufGlobals.push_back(ValueAsMetadata::get(ElemGV));
   }
+  assert(ElemIt == LayoutStruct->element_end() &&
+         "number of elements in layout struct does not match");
+
+  // add buffer metadata to the module
+  CGM.getModule()
+      .getOrInsertNamedMetadata("hlsl.cbs")
+      ->addOperand(MDNode::get(Ctx, BufGlobals));
 }
 
-void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) {
-  Buffers.emplace_back(Buffer(D));
-  addBufferDecls(D, Buffers.back());
+// Creates resource handle type for the HLSL buffer declaration
+static const clang::HLSLAttributedResourceType *
+createBufferHandleType(const HLSLBufferDecl *BufDecl) {
+  ASTContext &AST = BufDecl->getASTContext();
+  QualType QT = AST.getHLSLAttributedResourceType(
+      AST.HLSLResourceTy,
+      QualType(BufDecl->getLayoutStruct()->getTypeForDecl(), 0),
+      HLSLAttributedResourceType::Attributes(ResourceClass::CBuffer));
+  return cast<HLSLAttributedResourceType>(QT.getTypePtr());
+}
+
+static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
+                                 SmallVector<unsigned> &Layout) {
+  assert(Layout.empty() && "expected empty vector for layout");
+  assert(BufDecl->hasValidPackoffset());
+
+  for (Decl *D : BufDecl->decls()) {
+    if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) {
+      continue;
+    }
+    VarDecl *VD = dyn_cast<VarDecl>(D);
+    if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant)
+      continue;
+    assert(VD->hasAttr<HLSLPackOffsetAttr>() &&
+           "expected packoffset attribute on every declaration");
+    size_t Offset = VD->getAttr<HLSLPackOffsetAttr>()->getOffsetInBytes();
+    Layout.push_back(Offset);
+  }
+}
+
+// Codegen for HLSLBufferDecl
+void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
+
+  assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet");
+
+  // create resource handle type for the buffer
+  const clang::HLSLAttributedResourceType *ResHandleTy =
+      createBufferHandleType(BufDecl);
+
+  // empty constant buffer is ignored
+  if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty())
+    return;
+
+  // create global variable for the constant buffer
+  SmallVector<unsigned> Layout;
+  if (BufDecl->hasValidPackoffset())
+    fillPackoffsetLayout(BufDecl, Layout);
+
+  llvm::TargetExtType *TargetTy =
+      cast<llvm::TargetExtType>(convertHLSLSpecificType(
+          ResHandleTy, BufDecl->hasValidPackoffset() ? &Layout : nullptr));
+  llvm::GlobalVariable *BufGV =
+      new GlobalVariable(TargetTy, /*isConstant*/ true,
+                         GlobalValue::LinkageTypes::ExternalLinkage, nullptr,
+                         llvm::formatv("{0}{1}", BufDecl->getName(),
+                                       BufDecl->isCBuffer() ? ".cb" : ".tb"),
+                         GlobalValue::NotThreadLocal);
+  CGM.getModule().insertGlobalVariable(BufGV);
+
+  // Add globals for constant buffer elements and create metadata nodes
+  emitBufferGlobalsAndMetadata(BufDecl, BufGV);
+
+  // Resource initialization
+  const HLSLResourceBindingAttr *RBA =
+      BufDecl->getAttr<HLSLResourceBindingAttr>();
+  // FIXME: handle implicit binding if no binding attribute is found
+  // (llvm/llvm-project#110722)
+  if (RBA)
+    createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(),
+                         RBA->getSpaceNumber());
+}
+
+llvm::TargetExtType *
+CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) {
+  const auto Entry = LayoutTypes.find(StructType);
+  if (Entry != LayoutTypes.end())
+    return Entry->getSecond();
+  return nullptr;
+}
+
+void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType,
+                                            llvm::TargetExtType *LayoutTy) {
+  assert(getHLSLBufferLayoutType(StructType) == nullptr &&
+         "layout type for this struct already exist");
+  LayoutTypes[StructType] = LayoutTy;
 }
 
 void CGHLSLRuntime::finishCodeGen() {
@@ -169,28 +263,8 @@ void CGHLSLRuntime::finishCodeGen() {
     addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
 
   generateGlobalCtorDtorCalls();
-
-  const DataLayout &DL = M.getDataLayout();
-
-  for (auto &Buf : Buffers) {
-    layoutBuffer(Buf, DL);
-    GlobalVariable *GV = replaceBuffer(Buf);
-    M.insertGlobalVariable(GV);
-    llvm::hlsl::ResourceClass RC = Buf.IsCBuffer
-                                       ? llvm::hlsl::ResourceClass::CBuffer
-                                       : llvm::hlsl::ResourceClass::SRV;
-    llvm::hlsl::ResourceKind RK = Buf.IsCBuffer
-                                      ? llvm::hlsl::ResourceKind::CBuffer
-                                      : llvm::hlsl::ResourceKind::TBuffer;
-    addBufferResourceAnnotation(GV, RC, RK, /*IsROV=*/false,
-                                llvm::hlsl::ElementType::Invalid, Buf.Binding);
-  }
 }
 
-CGHLSLRuntime::Buffer::Buffer(const HLSLBufferDecl *D)
-    : Name(D->getName()), IsCBuffer(D->isCBuffer()),
-      Binding(D->getAttr<HLSLResourceBindingAttr>()) {}
-
 void CGHLSLRuntime::addBufferResourceAnnotation(llvm::GlobalVariable *GV,
                                                 llvm::hlsl::ResourceClass RC,
                                                 llvm::hlsl::ResourceKind RK,
@@ -524,21 +598,15 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   }
 }
 
-// Returns true if the type is an HLSL resource class
-static bool isResourceRecordType(const clang::Type *Ty) {
-  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
-}
-
-static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD,
-                                 llvm::GlobalVariable *GV, unsigned Slot,
-                                 unsigned Space) {
+static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
+                                 unsigned Slot, unsigned Space) {
   LLVMContext &Ctx = CGM.getLLVMContext();
   llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx);
 
   llvm::Function *InitResFunc = llvm::Function::Create(
       llvm::FunctionType::get(CGM.VoidTy, false),
       llvm::GlobalValue::InternalLinkage,
-      ("_init_resource_" + VD->getName()).str(), CGM.getModule());
+      ("_init_resource_" + GV->getName()).str(), CGM.getModule());
   InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline);
 
   llvm::BasicBlock *EntryBB =
@@ -547,20 +615,15 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD,
   const DataLayout &DL = CGM.getModule().getDataLayout();
   Builder.SetInsertPoint(EntryBB);
 
-  const HLSLAttributedResourceType *AttrResType =
-      HLSLAttributedResourceType::findHandleTypeOnResource(
-          VD->getType().getTypePtr());
-
-  // FIXME: Only simple declarations of resources are supported for now.
-  // Arrays of resources or resources in user defined classes are
-  // not implemented yet.
-  assert(AttrResType != nullptr &&
-         "Resource class must have a handle of HLSLAttributedResourceType");
-
-  llvm::Type *TargetTy =
-      CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType);
-  assert(TargetTy != nullptr &&
-         "Failed to convert resource handle to target type");
+  // Make sure the global variable is resource handle (cbuffer) or
+  // resource class (=class where the first element is a resource handle).
+  llvm::Type *HandleTy = GV->getValueType();
+  assert((HandleTy->isTargetExtTy() ||
+          (HandleTy->isStructTy() &&
+           HandleTy->getStructElementType(0)->isTargetExtTy())) &&
+         "unexpected type of the global");
+  if (!HandleTy->isTargetExtTy())
+    HandleTy = HandleTy->getStructElementType(0);
 
   llvm::Value *Args[] = {
       llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */
@@ -572,9 +635,9 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD,
       llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */
   };
   llvm::Value *CreateHandle = Builder.CreateIntrinsic(
-      /*ReturnType=*/TargetTy,
+      /*ReturnType=*/HandleTy,
       CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr,
-      Twine(VD->getName()).concat("_h"));
+      Twine(GV->getName()).concat("_h"));
 
   llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0);
   Builder.CreateAlignedStore(CreateHandle, HandleRef,
@@ -601,8 +664,7 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
     // not implemented yet.
     return;
 
-  createResourceInitFn(CGM, VD, GV, RBA->getSlotNumber(),
-                       RBA->getSpaceNumber());
+  createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber());
 }
 
 llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 8767a2ddceb96..a9da42324a038 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -15,6 +15,7 @@
 #ifndef LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H
 #define LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H
 
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsDirectX.h"
@@ -46,20 +47,26 @@
     }                                                                          \
   }
 
+using ResourceClass = llvm::dxil::ResourceClass;
+
 namespace llvm {
 class GlobalVariable;
 class Function;
 class StructType;
+class Metadata;
 } // namespace llvm
 
 namespace clang {
+class NamedDecl;
 class VarDecl;
 class ParmVarDecl;
 class InitListExpr;
 class HLSLBufferDecl;
 class HLSLResourceBindingAttr;
 class Type;
+class RecordType;
 class DeclContext;
+class HLSLPackOffsetAttr;
 
 class FunctionDecl;
 
@@ -126,16 +133,6 @@ class CGHLSLRuntime {
     unsigned Space;
     BufferResBinding(HLSLResourceBindingAttr *Attr);
   };
-  struct Buffer {
-    Buffer(const HLSLBufferDecl *D);
-    llvm::StringRef Name;
-    // IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer).
-    bool IsCBuffer;
-    BufferResBinding Binding;
-    // Global variable and offset for each constant.
-    std::vector<std::pair<llvm::GlobalVariable *, unsigned>> Constants;
-    llvm::StructType *LayoutStruct = nullptr;
-  };
 
 protected:
   CodeGenModule &CGM;
@@ -147,7 +144,9 @@ class CGHLSLRuntime {
   CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {}
   virtual ~CGHLSLRuntime() {}
 
-  llvm::Type *convertHLSLSpecificType(const Type *T);
+  llvm::Type *
+  convertHLSLSpecificType(const Type *T,
+                          SmallVector<unsigned> *Packoffsets = nullptr);
 
   void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
   void generateGlobalCtorDtorCalls();
@@ -163,6 +162,10 @@ class CGHLSLRuntime {
 
   llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
+  llvm::TargetExtType *
+  getHLSLBufferLayoutType(const RecordType *LayoutStructTy);
+  void addHLSLBufferLayoutType(const RecordType *LayoutStructTy,
+                               llvm::TargetExtType *LayoutTy);
   void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
 
 private:
@@ -171,10 +174,11 @@ class CGHLSLRuntime {
                                    llvm::hlsl::ResourceKind RK, bool IsROV,
                                    llvm::hlsl::ElementType ET,
                                    BufferResBinding &Binding);
-  void addConstant(VarDecl *D, Buffer &CB);
-  void addBufferDecls(const DeclContext *DC, Buffer &CB);
+  void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
+                                    llvm::GlobalVariable *BufGV);
   llvm::Triple::ArchType getArch();
-  llvm::SmallVector<Buffer> Buffers;
+
+  llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
 };
 
 } // namespace CodeGen

diff  --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 868ec847b9634..05ab6671453f8 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -106,6 +106,7 @@ add_clang_library(clangCodeGen
   ConstantInitBuilder.cpp
   CoverageMappingGen.cpp
   ItaniumCXXABI.cpp
+  HLSLBufferLayoutBuilder.cpp
   LinkInModulesPass.cpp
   MacroPPCallbacks.cpp
   MicrosoftCXXABI.cpp

diff  --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
new file mode 100644
index 0000000000000..1ae00023ab2bc
--- /dev/null
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -0,0 +1,229 @@
+//===- HLSLBufferLayoutBuilder.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HLSLBufferLayoutBuilder.h"
+#include "CGHLSLRuntime.h"
+#include "CodeGenModule.h"
+#include "clang/AST/Type.h"
+
+//===----------------------------------------------------------------------===//
+// Implementation of constant buffer layout common between DirectX and
+// SPIR/SPIR-V.
+//===----------------------------------------------------------------------===//
+
+using namespace clang;
+using namespace clang::CodeGen;
+
+namespace {
+
+// Creates a new array type with the same dimentions but with the new
+// element type.
+static llvm::Type *
+createArrayWithNewElementType(CodeGenModule &CGM,
+                              const ConstantArrayType *ArrayType,
+                              llvm::Type *NewElemType) {
+  const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual();
+  if (ArrayElemType->isConstantArrayType())
+    NewElemType = createArrayWithNewElementType(
+        CGM, cast<const ConstantArrayType>(ArrayElemType), NewElemType);
+  return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize());
+}
+
+// Returns the size of a scalar or vector in bytes
+static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) {
+  assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy());
+  if (Ty->isVectorTy()) {
+    llvm::FixedVectorType *FVT = cast<llvm::FixedVectorType>(Ty);
+    return FVT->getNumElements() *
+           (FVT->getElementType()->getScalarSizeInBits() / 8);
+  }
+  return Ty->getScalarSizeInBits() / 8;
+}
+
+} // namespace
+
+namespace clang {
+namespace CodeGen {
+
+// Creates a layout type for given struct with HLSL constant buffer layout
+// taking into account Packoffsets, if provided.
+// Previously created layout types are cached by CGHLSLRuntime.
+//
+// The function iterates over all fields of the StructType (including base
+// classes) and calls layoutField to converts each field to its corresponding
+// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
+// structs (or arrays of structs) are converted to target layout types as well.
+llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
+    const RecordType *StructType,
+    const llvm::SmallVector<unsigned> *Packoffsets) {
+
+  // check if we already have the layout type for this struct
+  if (llvm::TargetExtType *Ty =
+          CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType))
+    return Ty;
+
+  SmallVector<unsigned> Layout;
+  SmallVector<llvm::Type *> LayoutElements;
+  unsigned Index = 0; // packoffset index
+  unsigned EndOffset = 0;
+
+  // reserve first spot in the layout vector for buffer size
+  Layout.push_back(0);
+
+  // iterate over all fields of the record, including fields on base classes
+  llvm::SmallVector<const RecordType *> RecordTypes;
+  RecordTypes.push_back(StructType);
+  while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
+    CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
+    assert(D->getNumBases() == 1 &&
+           "HLSL doesn't support multiple inheritance");
+    RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
+  }
+  while (!RecordTypes.empty()) {
+    const RecordType *RT = RecordTypes.back();
+    RecordTypes.pop_back();
+
+    for (const auto *FD : RT->getDecl()->fields()) {
+      assert(!Packoffsets || Index < Packoffsets->size() &&
+                                 "number of elements in layout struct does not "
+                                 "match number of packoffset annotations");
+
+      if (!layoutField(FD, EndOffset, Layout, LayoutElements,
+                       Packoffsets ? (*Packoffsets)[Index] : -1))
+        return nullptr;
+      Index++;
+    }
+  }
+
+  // set the size of the buffer
+  Layout[0] = EndOffset;
+
+  // create the layout struct type; anonymous struct have empty name but
+  // non-empty qualified name
+  const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl();
+  std::string Name =
+      Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString();
+  llvm::StructType *StructTy =
+      llvm::StructType::create(LayoutElements, Name, true);
+
+  // create target layout type
+  llvm::TargetExtType *NewLayoutTy = llvm::TargetExtType::get(
+      CGM.getLLVMContext(), LayoutTypeName, {StructTy}, Layout);
+  if (NewLayoutTy)
+    CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy);
+  return NewLayoutTy;
+}
+
+// The function converts a single field of HLSL Buffer to its corresponding
+// LLVM type and calculates it's layout. Any embedded structs (or
+// arrays of structs) are converted to target layout types as well.
+// The converted type is appended to the LayoutElements list, the element
+// offset is added to the Layout list and the EndOffset updated to the offset
+// just after the lay-ed out element (which is basically the size of the
+// buffer).
+// Returns true if the conversion was successful.
+// The packoffset parameter contains the field's layout offset provided by the
+// user or -1 if there was no packoffset (or register(cX)) annotation.
+bool HLSLBufferLayoutBuilder::layoutField(
+    const FieldDecl *FD, unsigned &EndOffset, SmallVector<unsigned> &Layout,
+    SmallVector<llvm::Type *> &LayoutElements, int Packoffset) {
+
+  // Size of element; for arrays this is a size of a single element in the
+  // array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
+  // ElemSize.
+  unsigned ElemSize = 0;
+  unsigned ElemOffset = 0;
+  unsigned ArrayCount = 1;
+  unsigned ArrayStride = 0;
+
+  const unsigned BufferRowAlign = 16U;
+  unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign);
+
+  llvm::Type *ElemLayoutTy = nullptr;
+  QualType FieldTy = FD->getType();
+
+  if (FieldTy->isConstantArrayType()) {
+    // Unwrap array to find the element type and get combined array size.
+    QualType Ty = FieldTy;
+    while (Ty->isConstantArrayType()) {
+      const ConstantArrayType *ArrayTy = cast<ConstantArrayType>(Ty);
+      ArrayCount *= ArrayTy->getSExtSize();
+      Ty = ArrayTy->getElementType();
+    }
+    // For array of structures, create a new array with a layout type
+    // instead of the structure type.
+    if (Ty->isStructureType()) {
+      llvm::Type *NewTy =
+          cast<llvm::TargetExtType>(createLayoutType(Ty->getAsStructureType()));
+      if (!NewTy)
+        return false;
+      assert(isa<llvm::TargetExtType>(NewTy) && "expected target type");
+      ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0);
+      ElemLayoutTy = createArrayWithNewElementType(
+          CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy);
+    } else {
+      // Array of vectors or scalars
+      ElemSize =
+          getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
+      ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
+    }
+    ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign);
+    ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
+
+  } else if (FieldTy->isStructureType()) {
+    // Create a layout type for the structure
+    ElemLayoutTy = createLayoutType(FieldTy->getAsStructureType());
+    if (!ElemLayoutTy)
+      return false;
+    assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type");
+    ElemSize = cast<llvm::TargetExtType>(ElemLayoutTy)->getIntParameter(0);
+    ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
+
+  } else {
+    // scalar or vector - find element size and alignment
+    unsigned Align = 0;
+    ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
+    if (ElemLayoutTy->isVectorTy()) {
+      // align vectors by sub element size
+      const llvm::FixedVectorType *FVT =
+          cast<llvm::FixedVectorType>(ElemLayoutTy);
+      unsigned SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8;
+      ElemSize = FVT->getNumElements() * SubElemSize;
+      Align = SubElemSize;
+    } else {
+      assert(ElemLayoutTy->isIntegerTy() || ElemLayoutTy->isFloatingPointTy());
+      ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8;
+      Align = ElemSize;
+    }
+
+    // calculate or get element offset for the vector or scalar
+    if (Packoffset != -1) {
+      ElemOffset = Packoffset;
+    } else {
+      ElemOffset = llvm::alignTo(EndOffset, Align);
+      // if the element does not fit, move it to the next row
+      if (ElemOffset + ElemSize > NextRowOffset)
+        ElemOffset = NextRowOffset;
+    }
+  }
+
+  // Update end offset of the layout; do not update it if the EndOffset
+  // is already bigger than the new value (which may happen with unordered
+  // packoffset annotations)
+  unsigned NewEndOffset =
+      ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
+  EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);
+
+  // add the layout element and offset to the lists
+  Layout.push_back(ElemOffset);
+  LayoutElements.push_back(ElemLayoutTy);
+  return true;
+}
+
+} // namespace CodeGen
+} // namespace clang

diff  --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
new file mode 100644
index 0000000000000..57bb17c557b9c
--- /dev/null
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
@@ -0,0 +1,48 @@
+//===- HLSLBufferLayoutBuilder.h ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/DerivedTypes.h"
+
+namespace clang {
+class RecordType;
+class FieldDecl;
+
+namespace CodeGen {
+class CodeGenModule;
+
+//===----------------------------------------------------------------------===//
+// Implementation of constant buffer layout common between DirectX and
+// SPIR/SPIR-V.
+//===----------------------------------------------------------------------===//
+
+class HLSLBufferLayoutBuilder {
+private:
+  CodeGenModule &CGM;
+  llvm::StringRef LayoutTypeName;
+
+public:
+  HLSLBufferLayoutBuilder(CodeGenModule &CGM, llvm::StringRef LayoutTypeName)
+      : CGM(CGM), LayoutTypeName(LayoutTypeName) {}
+
+  // Returns LLVM target extension type with the name LayoutTypeName
+  // for given structure type and layout data. The first number in
+  // the Layout is the size followed by offsets for each struct element.
+  llvm::TargetExtType *
+  createLayoutType(const RecordType *StructType,
+                   const llvm::SmallVector<unsigned> *Packoffsets = nullptr);
+
+private:
+  bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset,
+                   llvm::SmallVector<unsigned> &Layout,
+                   llvm::SmallVector<llvm::Type *> &LayoutElements,
+                   int Packoffset);
+};
+
+} // namespace CodeGen
+} // namespace clang

diff  --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index 4a66683a3b91f..86057c14a549e 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -439,7 +439,9 @@ class TargetCodeGenInfo {
   }
 
   /// Return an LLVM type that corresponds to a HLSL type
-  virtual llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const {
+  virtual llvm::Type *
+  getHLSLType(CodeGenModule &CGM, const Type *T,
+              const SmallVector<unsigned> *Packoffsets = nullptr) const {
     return nullptr;
   }
 

diff  --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp
index 7935f7ae37004..77091eb45f5cf 100644
--- a/clang/lib/CodeGen/Targets/DirectX.cpp
+++ b/clang/lib/CodeGen/Targets/DirectX.cpp
@@ -7,8 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "ABIInfoImpl.h"
+#include "CodeGenModule.h"
+#include "HLSLBufferLayoutBuilder.h"
 #include "TargetInfo.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Type.h"
 
 using namespace clang;
 using namespace clang::CodeGen;
@@ -24,11 +29,14 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo {
   DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
       : TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(CGT)) {}
 
-  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(
+      CodeGenModule &CGM, const Type *T,
+      const SmallVector<unsigned> *Packoffsets = nullptr) const override;
 };
 
-llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
-                                                  const Type *Ty) const {
+llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(
+    CodeGenModule &CGM, const Type *Ty,
+    const SmallVector<unsigned> *Packoffsets) const {
   auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
   if (!ResType)
     return nullptr;
@@ -56,9 +64,19 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
 
     return llvm::TargetExtType::get(Ctx, TypeName, {ElemType}, Ints);
   }
-  case llvm::dxil::ResourceClass::CBuffer:
-    llvm_unreachable("dx.CBuffer handles are not implemented yet");
-    break;
+  case llvm::dxil::ResourceClass::CBuffer: {
+    QualType ContainedTy = ResType->getContainedType();
+    if (ContainedTy.isNull() || !ContainedTy->isStructureType())
+      return nullptr;
+
+    llvm::Type *BufferLayoutTy =
+        HLSLBufferLayoutBuilder(CGM, "dx.Layout")
+            .createLayoutType(ContainedTy->getAsStructureType(), Packoffsets);
+    if (!BufferLayoutTy)
+      return nullptr;
+
+    return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {BufferLayoutTy});
+  }
   case llvm::dxil::ResourceClass::Sampler:
     llvm_unreachable("dx.Sampler handles are not implemented yet");
     break;

diff  --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp
index b81ed29a5159b..c94db31ae1a89 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,7 +52,9 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
-  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getHLSLType(
+      CodeGenModule &CGM, const Type *Ty,
+      const SmallVector<unsigned> *Packoffsets = nullptr) const override;
   llvm::Type *getSPIRVImageTypeFromHLSLResource(
       const HLSLAttributedResourceType::Attributes &attributes,
       llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
@@ -367,8 +369,9 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
-llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
-                                                     const Type *Ty) const {
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(
+    CodeGenModule &CGM, const Type *Ty,
+    const SmallVector<unsigned> *Packoffsets) const {
   auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
   if (!ResType)
     return nullptr;

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 362df485a025c..d95763b22a819 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14295,6 +14295,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
     if (getLangOpts().OpenCL &&
         Var->getType().getAddressSpace() == LangAS::opencl_local)
       return;
+
+    // In HLSL, objects in the hlsl_constant address space are initialized
+    // externally, 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

diff  --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 20275ded8a561..502a1429ff6e3 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -239,6 +239,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
 
   // Make sure there is no overlap in packoffset - sort PackOffsetVec by offset
   // and compare adjacent values.
+  bool IsValid = true;
   ASTContext &Context = S.getASTContext();
   std::sort(PackOffsetVec.begin(), PackOffsetVec.end(),
             [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS,
@@ -257,8 +258,10 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
       VarDecl *NextVar = PackOffsetVec[i + 1].first;
       S.Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap)
           << NextVar << Var;
+      IsValid = false;
     }
   }
+  BufDecl->setHasValidPackoffset(IsValid);
 }
 
 // Returns true if the array has a zero size = if any of the dimensions is 0
@@ -500,7 +503,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
     }
   }
   LS->completeDefinition();
-  BufDecl->addDecl(LS);
+  BufDecl->addLayoutStruct(LS);
 }
 
 // Handle end of cbuffer/tbuffer declaration

diff  --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl
deleted file mode 100644
index 825e7b8161a60..0000000000000
--- a/clang/test/CodeGenHLSL/cbuf.hlsl
+++ /dev/null
@@ -1,33 +0,0 @@
-// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-// CHECK: @a = external addrspace(2) externally_initialized global float, align 4
-// CHECK: @b = external addrspace(2) externally_initialized global double, align 8
-// CHECK: @c = external addrspace(2) externally_initialized global float, align 4
-// CHECK: @d = external addrspace(2) externally_initialized global double, align 8
-
-// CHECK: @[[CB:.+]] = external constant { float, double }
-cbuffer A : register(b0, space2) {
-  float a;
-  double b;
-}
-
-// CHECK: @[[TB:.+]] = external constant { float, double }
-tbuffer A : register(t2, space1) {
-  float c;
-  double d;
-}
-
-float foo() {
-// CHECK: load float, ptr addrspace(2) @a, align 4
-// CHECK: load double, ptr addrspace(2) @b, align 8
-// CHECK: load float, ptr addrspace(2) @c, align 4
-// CHECK: load double, ptr addrspace(2) @d, align 8
-  return a + b + c*d;
-}
-
-// CHECK: !hlsl.cbufs = !{![[CBMD:[0-9]+]]}
-// CHECK: ![[CBMD]] = !{ptr @[[CB]], i32 13, i32 0, i1 false, i32 0, i32 2}

diff  --git a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl b/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
deleted file mode 100644
index 13c401d428331..0000000000000
--- a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
+++ /dev/null
@@ -1,29 +0,0 @@
-// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-// Make sure cbuffer inside namespace works.
-
-// CHECK: @_ZN2n02n11aE = external addrspace(2) externally_initialized global float, align 4
-// CHECK: @_ZN2n01bE = external addrspace(2) externally_initialized global float, align 4
-
-// CHECK: @[[CB:.+]] = external constant { float }
-// CHECK: @[[TB:.+]] = external constant { float }
-namespace n0 {
-namespace n1 {
-  cbuffer A {
-    float a;
-  }
-}
-  tbuffer B {
-    float b;
-  }
-}
-
-float foo() {
-// CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4
-// CHECK: load float, ptr addrspace(2) @_ZN2n01bE, align 4
-  return n0::n1::a + n0::b;
-}

diff  --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl
new file mode 100644
index 0000000000000..38093c6dfacd7
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer.hlsl
@@ -0,0 +1,197 @@
+// 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_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }>
+// CHECK: %__cblayout_CBVectors = type <{ <3 x float>, <3 x double>, <2 x half>, <3 x i64>, <4 x i32>, <3 x i16>, <3 x i64> }>
+// CHECK: %__cblayout_CBArrays = type <{ [3 x float], [2 x <3 x double>], [2 x [2 x half]], [3 x i64], [2 x [3 x [4 x <4 x i32>]]], [1 x i16], [2 x i64], [4 x i32] }>
+// CHECK: %__cblayout_CBStructs = type <{ target("dx.Layout", %A, 8, 0), target("dx.Layout", %B, 14, 0, 8), 
+// CHECK-SAME: target("dx.Layout", %C, 24, 0, 16), [5 x target("dx.Layout", %A, 8, 0)], 
+// CHECK-SAME: target("dx.Layout", %__cblayout_D, 94, 0), half, <3 x i16> }>
+
+// CHECK: %A = type <{ <2 x float> }>
+// CHECK: %B = type <{ <2 x float>, <3 x i16> }>
+// CHECK: %C = type <{ i32, target("dx.Layout", %A, 8, 0) }>
+// CHECK: %__cblayout_D = type <{ [2 x [3 x target("dx.Layout", %B, 14, 0, 8)]] }>
+
+// CHECK: %__cblayout_CBMix = type <{ [2 x target("dx.Layout", %Test, 8, 0, 4)], float, [3 x [2 x <2 x float>]], float,
+// CHECK-SAME: target("dx.Layout", %anon, 4, 0), double, target("dx.Layout", %anon.0, 8, 0), float, <1 x double>, i16 }>
+
+// CHECK: %Test = type <{ float, float }>
+// CHECK: %anon = type <{ float }>
+// CHECK: %anon.0 = type <{ <2 x i32> }>
+
+cbuffer CBScalars : register(b1, space5) {
+  float a1;
+  double a2;
+  float16_t a3;
+  uint64_t a4;
+  int a5;
+  uint16_t a6;
+  bool a7;
+  int64_t a8;
+}
+
+// CHECK: @CBScalars.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 
+// CHECK-SAME: 56, 0, 8, 16, 24, 32, 36, 40, 48))
+// CHECK: @a1 = external addrspace(2) global float, align 4
+// CHECK: @a2 = external addrspace(2) global double, align 8
+// CHECK: @a3 = external addrspace(2) global half, align 2
+// CHECK: @a4 = external addrspace(2) global i64, align 8
+// CHECK: @a5 = external addrspace(2) global i32, align 4
+// CHECK: @a6 = external addrspace(2) global i16, align 2
+// CHECK: @a7 = external addrspace(2) global i32, align 4
+// CHECK: @a8 = external addrspace(2) global i64, align 8
+
+cbuffer CBVectors {
+  float3 b1;
+  double3 b2;
+  float16_t2 b3;
+  uint64_t3 b4;
+  int4 b5;
+  uint16_t3 b6;
+  int64_t3 b7;
+  // FIXME: add a bool vectors after llvm-project/llvm#91639 is added
+}
+
+// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBVectors, 
+// CHECK-SAME: 136, 0, 16, 40, 48, 80, 96, 112))
+// CHECK: @b1 = external addrspace(2) global <3 x float>, align 16
+// CHECK: @b2 = external addrspace(2) global <3 x double>, align 32
+// CHECK: @b3 = external addrspace(2) global <2 x half>, align 4
+// CHECK: @b4 = external addrspace(2) global <3 x i64>, align 32
+// CHECK: @b5 = external addrspace(2) global <4 x i32>, align 16
+// CHECK: @b6 = external addrspace(2) global <3 x i16>, align 8
+// CHECK: @b7 = external addrspace(2) global <3 x i64>, align 32
+
+cbuffer CBArrays : register(b2) {
+  float c1[3];
+  double3 c2[2];
+  float16_t c3[2][2];
+  uint64_t c4[3];
+  int4 c5[2][3][4];
+  uint16_t c6[1];
+  int64_t c7[2];
+  bool c8[4];
+}
+
+// CHECK: @CBArrays.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 
+// CHECK-SAME: 708, 0, 48, 112, 176, 224, 608, 624, 656))
+// CHECK: @c1 = external addrspace(2) global [3 x float], align 4
+// CHECK: @c2 = external addrspace(2) global [2 x <3 x double>], align 32
+// CHECK: @c3 = external addrspace(2) global [2 x [2 x half]], align 2
+// CHECK: @c4 = external addrspace(2) global [3 x i64], align 8
+// CHECK: @c5 = external addrspace(2) global [2 x [3 x [4 x <4 x i32>]]], align 16
+// CHECK: @c6 = external addrspace(2) global [1 x i16], align 2
+// CHECK: @c7 = external addrspace(2) global [2 x i64], align 8
+// CHECK: @c8 = external addrspace(2) global [4 x i32], align 4
+
+struct Empty {};
+
+struct A {
+  float2 f1;
+};
+
+struct B : A {
+  uint16_t3 f2;
+};
+
+struct C {
+  int i;
+  A f3;
+};
+
+struct D {
+  B array_of_B[2][3];
+  Empty es;
+};
+
+// CHECK: @CBStructs.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBStructs, 
+// CHECK-SAME: 246, 0, 16, 32, 64, 144, 238, 240))
+// CHECK: @a = external addrspace(2) global target("dx.Layout", %A, 8, 0), align 8
+// CHECK: @b = external addrspace(2) global target("dx.Layout", %B, 14, 0, 8), align 8
+// CHECK: @c = external addrspace(2) global target("dx.Layout", %C, 24, 0, 16), align 8
+// CHECK: @array_of_A = external addrspace(2) global [5 x target("dx.Layout", %A, 8, 0)], align 8
+// CHECK: @d = external addrspace(2) global target("dx.Layout", %__cblayout_D, 94, 0), align 8
+// CHECK: @e = external addrspace(2) global half, align 2
+// CHECK: @f = external addrspace(2) global <3 x i16>, align 8
+
+cbuffer CBStructs {
+  A a;
+  B b;
+  C c;
+  A array_of_A[5];
+  D d;
+  half e;
+  uint16_t3 f;
+};
+
+struct Test {
+    float a, b;
+};
+
+// CHECK: @CBMix.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBMix,
+// CHECK-SAME: 170, 0, 24, 32, 120, 128, 136, 144, 152, 160, 168))
+// CHECK: @test = external addrspace(2) global [2 x target("dx.Layout", %Test, 8, 0, 4)], align 4
+// CHECK: @f1 = external addrspace(2) global float, align 4
+// CHECK: @f2 = external addrspace(2) global [3 x [2 x <2 x float>]], align 8
+// CHECK: @f3 = external addrspace(2) global float, align 4
+// CHECK: @f4 = external addrspace(2) global target("dx.Layout", %anon, 4, 0), align 4
+// CHECK: @f5 = external addrspace(2) global double, align 8
+// CHECK: @f6 = external addrspace(2) global target("dx.Layout", %anon.0, 8, 0), align 8
+// CHECK: @f7 = external addrspace(2) global float, align 4
+// CHECK: @f8 = external addrspace(2) global <1 x double>, align 8
+// CHECK: @f9 = external addrspace(2) global i16, align 2
+
+cbuffer CBMix {
+    Test test[2];
+    float f1;
+    float2 f2[3][2];
+    float f3;
+    struct { float c; } f4;
+    double f5;
+    struct { int2 i; } f6;
+    float f7;
+    vector<double,1> f8;
+    uint16_t f9;
+};  
+
+// CHECK: define internal void @_init_resource_CBScalars.cb()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48))
+// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBScalarss_56_0_8_16_24_32_36_40_48tt(i32 5, i32 1, i32 1, i32 0, i1 false)
+// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) %CBScalars.cb_h, ptr @CBScalars.cb, align 4
+
+// CHECK: define internal void @_init_resource_CBArrays.cb()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656))
+// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBArrayss_708_0_48_112_176_224_608_624_656tt(i32 0, i32 2, i32 1, i32 0, i1 false)
+// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656)) %CBArrays.cb_h, ptr @CBArrays.cb, align 4
+
+RWBuffer<float> Buf;
+
+[numthreads(4,1,1)]
+void main() {
+  Buf[0] = a1 + b1.z + c1[2] + a.f1.y + f1;
+}
+
+// CHECK: define internal void @_GLOBAL__sub_I_cbuffer.hlsl()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @_init_resource_CBScalars.cb()
+// CHECK-NEXT: call void @_init_resource_CBArrays.cb()
+
+// CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]}
+
+// CHECK: ![[CBSCALARS]] = !{ptr @CBScalars.cb, ptr addrspace(2) @a1, ptr addrspace(2) @a2, ptr addrspace(2) @a3, ptr addrspace(2) @a4,
+// CHECK-SAME: ptr addrspace(2) @a5, ptr addrspace(2) @a6, ptr addrspace(2) @a7, ptr addrspace(2) @a8}
+
+// CHECK: ![[CBVECTORS]] = !{ptr @CBVectors.cb, ptr addrspace(2) @b1, ptr addrspace(2) @b2, ptr addrspace(2) @b3, ptr addrspace(2) @b4,
+// CHECK-SAME: ptr addrspace(2) @b5, ptr addrspace(2) @b6, ptr addrspace(2) @b7}
+
+// CHECK: ![[CBARRAYS]] = !{ptr @CBArrays.cb, ptr addrspace(2) @c1, ptr addrspace(2) @c2, ptr addrspace(2) @c3, ptr addrspace(2) @c4, 
+// CHECK-SAME: ptr addrspace(2) @c5, ptr addrspace(2) @c6, ptr addrspace(2) @c7, ptr addrspace(2) @c8}
+
+// CHECK: ![[CBSTRUCTS]] = !{ptr @CBStructs.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c, ptr addrspace(2) @array_of_A, 
+// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @f}
+
+// CHECK: ![[CBMIX]] = !{ptr @CBMix.cb, ptr addrspace(2) @test, ptr addrspace(2) @f1, ptr addrspace(2) @f2, ptr addrspace(2) @f3,
+// CHECK-SAME: ptr addrspace(2) @f4, ptr addrspace(2) @f5, ptr addrspace(2) @f6, ptr addrspace(2) @f7, ptr addrspace(2) @f8, ptr addrspace(2) @f9}

diff  --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
new file mode 100644
index 0000000000000..393ca3825c638
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+// Make sure cbuffer inside namespace works.
+
+// CHECK: %"n0::n1::__cblayout_A" = type <{ float }>
+// CHECK: %"n0::__cblayout_B" = type <{ float }>
+// CHECK: %"n0::n2::__cblayout_C" = type <{ float, target("dx.Layout", %"n0::Foo", 4, 0) }>
+// CHECK: %"n0::Foo" = type <{ float }>
+
+// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n1::__cblayout_A", 4, 0))
+// CHECK: @_ZN2n02n11aE = external addrspace(2) global float, align 4
+
+// CHECK: @B.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::__cblayout_B", 4, 0))
+// CHECK: @_ZN2n01aE = external addrspace(2) global float, align 4
+
+// CHECK: @C.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n2::__cblayout_C", 20, 0, 16))
+// CHECK: @_ZN2n02n21aE = external addrspace(2) global float, align 4
+// CHECK: external addrspace(2) global target("dx.Layout", %"n0::Foo", 4, 0), align 4
+
+namespace n0 {
+  struct Foo {
+    float f;
+  };
+
+  namespace n1 {
+    cbuffer A {
+      float a;
+    }
+  }
+  cbuffer B {
+    float a;
+  }
+  namespace n2 {
+    cbuffer C {
+      float a;
+      Foo b;
+    }
+  }
+}
+
+float foo() {
+  // CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4
+  // CHECK: load float, ptr addrspace(2) @_ZN2n01aE, align 4
+  // CHECK: load float, ptr addrspace(2) @_ZN2n02n21aE, align 4
+  return n0::n1::a + n0::a + n0::n2::a;
+}
+
+[numthreads(4,1,1)]
+void main() {}
+
+// CHECK: !hlsl.cbs = !{![[A:[0-9]+]], ![[B:[0-9]+]], ![[C:[0-9]+]]}
+// CHECK: [[A]] = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE}
+// CHECK: [[B]] = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE}
+// CHECK: [[C]] = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE}

diff  --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
new file mode 100644
index 0000000000000..870593986a976
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-compute %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }>
+
+// CHECK: @CB.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
+// CHECK: @a = external addrspace(2) global float, align 4
+// CHECK: @b = external addrspace(2) global double, align 8
+// CHECK: @c = external addrspace(2) global <2 x i32>, align 8
+
+cbuffer CB : register(b1, space3) {
+  float a : packoffset(c1.x);
+  double b : packoffset(c10.z);
+  int2 c : packoffset(c5.z);
+}
+
+// CHECK: define internal void @_init_resource_CB.cb()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
+// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBs_176_16_168_88tt(i32 3, i32 1, i32 1, i32 0, i1 false)
+
+float foo() {
+  // CHECK: load float, ptr addrspace(2) @a, align 4
+  // CHECK: load double, ptr addrspace(2) @b, align 8
+  return a + b;
+}
+// CHECK: define internal void @_GLOBAL__sub_I_cbuffer_with_packoffset.hlsl()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @_init_resource_CB.cb()
+
+[numthreads(4,1,1)]
+void main() {
+  foo();
+}
+
+// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]}
+// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}

diff  --git a/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl
new file mode 100644
index 0000000000000..99f40d8fc93d7
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+// CHECK: %__cblayout_A = type <{ float }>
+
+// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_A, 4, 0))
+// CHECK: @a = external addrspace(2) global float, align 4
+// CHECK-DAG: @_ZL1b = internal global float 3.000000e+00, align 4
+// CHECK-NOT: @B.cb
+
+cbuffer A {
+  float a;
+  static float b = 3;
+  float foo() { return a + b; }
+}
+
+cbuffer B {
+  // intentionally empty
+}
+
+// CHECK: define {{.*}} float @_Z3foov() #0 {
+// CHECK: load float, ptr addrspace(2) @a, align 4
+
+extern float bar() {
+  return foo();
+}
+
+// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]}
+// CHECK: ![[CB]] = !{ptr @A.cb, ptr addrspace(2) @a}

diff  --git a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl b/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl
deleted file mode 100644
index 25f51cce2017d..0000000000000
--- a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl
+++ /dev/null
@@ -1,22 +0,0 @@
-// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-
-cbuffer A {
-  // CHECK: @a = external addrspace(2) externally_initialized global float, align 4
-  float a;
-  // CHECK: @_ZL1b = internal global float 3.000000e+00, align 4
-  static float b = 3;
-  float foo() { return a + b; }
-}
-// CHECK: @[[CB:.+]] = external constant { float }
-
-// CHECK:define {{.*}} float @_Z3foov()
-// CHECK:load float, ptr addrspace(2) @a, align 4
-// CHECK:load float, ptr @_ZL1b, align 4
-
-float bar() {
-  return foo();
-}


        


More information about the cfe-commits mailing list