[llvm-branch-commits] [clang] [HLSL] Constant buffers codegen (PR #124886)
Helena Kotas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Feb 4 18:01:33 PST 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/124886
>From cf08adb6b9e181613e81d2cfbadbbb68e645fe33 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 28 Jan 2025 14:46:26 -0800
Subject: [PATCH 1/4] [HLSL] Translate cbuffer declarations to target type
dx.CBuffer - initial commit
---
clang/include/clang/AST/Decl.h | 6 +
clang/include/clang/AST/Type.h | 4 +-
clang/lib/AST/Decl.cpp | 17 +-
clang/lib/CodeGen/CGHLSLRuntime.cpp | 409 ++++++++++++------
clang/lib/CodeGen/CGHLSLRuntime.h | 31 +-
clang/lib/CodeGen/Targets/DirectX.cpp | 11 +-
clang/lib/Sema/SemaHLSL.cpp | 3 +
clang/test/CodeGenHLSL/cbuf.hlsl | 33 --
clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl | 29 --
clang/test/CodeGenHLSL/cbuffer.hlsl | 200 +++++++++
.../CodeGenHLSL/cbuffer_and_namespaces.hlsl | 63 +++
.../CodeGenHLSL/cbuffer_with_packoffset.hlsl | 40 ++
...uffer_with_static_global_and_function.hlsl | 32 ++
clang/test/CodeGenHLSL/resource-bindings.hlsl | 4 +
.../static_global_and_function_in_cb.hlsl | 22 -
15 files changed, 679 insertions(+), 225 deletions(-)
delete mode 100644 clang/test/CodeGenHLSL/cbuf.hlsl
delete mode 100644 clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
create mode 100644 clang/test/CodeGenHLSL/cbuffer.hlsl
create mode 100644 clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
create mode 100644 clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
create mode 100644 clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl
delete mode 100644 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 16403774e72b31..e1c7e3817699ce 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5032,6 +5032,9 @@ 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 HasPackoffset;
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
@@ -5052,6 +5055,9 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
SourceLocation getRBraceLoc() const { return RBraceLoc; }
void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
bool isCBuffer() const { return IsCBuffer; }
+ void setHasPackoffset(bool PO) { HasPackoffset = PO; }
+ bool hasPackoffset() const { return HasPackoffset; }
+ const CXXRecordDecl *getLayoutStruct() const;
// 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 1d9743520654eb..c3ff7ebd88516c 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 beb5fcaefac535..fa7d03354a9937 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)
@@ -5713,7 +5717,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), HasPackoffset(false) {}
HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
DeclContext *LexicalParent, bool CBuffer,
@@ -5743,6 +5747,17 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
SourceLocation(), SourceLocation());
}
+const CXXRecordDecl *HLSLBufferDecl::getLayoutStruct() const {
+ // Layout struct is the last decl in the HLSLBufferDecl.
+ if (CXXRecordDecl *RD = llvm::dyn_cast_or_null<CXXRecordDecl>(LastDecl)) {
+ assert(RD->getName().starts_with(
+ ("__cblayout_" + getIdentifier()->getName()).str()) &&
+ "expected buffer layout struct");
+ return RD;
+ }
+ llvm_unreachable("HLSL buffer is missing a layout struct");
+}
+
//===----------------------------------------------------------------------===//
// ImportDecl Implementation
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 2ce54cc3c52efa..d3d9d426643596 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -16,15 +16,20 @@
#include "CGDebugInfo.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
#include "clang/Basic/TargetOptions.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;
@@ -32,6 +37,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) {
@@ -54,54 +62,11 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey);
DXILValMD->addOperand(Val);
}
+
void addDisableOptimizations(llvm::Module &M) {
StringRef Key = "dx.disable_optimizations";
M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, Key, 1);
}
-// 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
@@ -119,48 +84,280 @@ 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;
+}
+
+// Returns true if the type is an HLSL resource class or an array of
+// HLSL resource classes
+static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) {
+ while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
+ Ty = CAT->getArrayElementTypeNoTypeQual();
+ return isResourceRecordType(Ty);
+}
+
+static ConstantAsMetadata *getConstIntMetadata(LLVMContext &Ctx, uint32_t value,
+ bool isSigned = false) {
+ return ConstantAsMetadata::get(
+ ConstantInt::get(Ctx, llvm::APInt(32, value, isSigned)));
+}
+
+static unsigned getScalarOrVectorSize(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;
+}
+
+size_t
+CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) {
+ assert(StructTy->isStructTy());
+
+ // check if we already have a side for this struct
+ auto SizeIt = StructSizesForBuffer.find(StructTy);
+ if (SizeIt != StructSizesForBuffer.end())
+ return SizeIt->getSecond();
+
+ // if not, calculate the struct layout and add it to metadata
+ LLVMContext &Ctx = CGM.getLLVMContext();
+ SmallVector<llvm::Metadata *> LayoutItems;
+ LayoutItems.push_back(MDString::get(Ctx, StructTy->getName()));
+
+ size_t StructSize = 0;
+ LayoutItems.push_back(nullptr); // reserve one slot for the buffer size
+
+ for (llvm::Type *ElTy : StructTy->elements())
+ addLayoutInfoForBufferElement(StructSize, LayoutItems, ElTy);
+
+ // set the size of the buffer
+ LayoutItems[1] = getConstIntMetadata(Ctx, StructSize);
+
+ // add the struct layout info to metadata
+ MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems);
+ CGM.getModule()
+ .getOrInsertNamedMetadata("hlsl.cblayouts")
+ ->addOperand(LayoutMDNode);
+
+ // add struct size to list and return it
+ StructSizesForBuffer[StructTy] = StructSize;
+ return StructSize;
+}
+
+void CGHLSLRuntime::addLayoutInfoForBufferElement(
+ size_t &EndOffset, SmallVector<llvm::Metadata *> &LayoutItems,
+ llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr) {
+
+ // calculate element offset and size; for arrays also calculate array
+ // element count and stride
+ size_t ElemOffset = 0;
+ size_t ElemSize = 0;
+ size_t ArrayCount = 1;
+ size_t ArrayStride = 0;
+ size_t NextRowOffset = llvm::alignTo(EndOffset, 16U);
+
+ if (LayoutTy->isArrayTy()) {
+ llvm::Type *Ty = LayoutTy;
+ while (Ty->isArrayTy()) {
+ ArrayCount *= Ty->getArrayNumElements();
+ Ty = Ty->getArrayElementType();
+ }
+ ElemSize =
+ Ty->isStructTy()
+ ? getOrCalculateStructSizeForBuffer(cast<llvm::StructType>(Ty))
+ : getScalarOrVectorSize(Ty);
+ ArrayStride = llvm::alignTo(ElemSize, 16U);
+ ElemOffset =
+ PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset;
+
+ } else if (LayoutTy->isStructTy()) {
+ ElemOffset =
+ PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset;
+ ElemSize =
+ getOrCalculateStructSizeForBuffer(cast<llvm::StructType>(LayoutTy));
+
+ } else {
+ size_t Align = 0;
+ if (LayoutTy->isVectorTy()) {
+ llvm::FixedVectorType *FVT = cast<llvm::FixedVectorType>(LayoutTy);
+ size_t SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8;
+ ElemSize = FVT->getNumElements() * SubElemSize;
+ Align = SubElemSize;
+ } else {
+ assert(LayoutTy->isIntegerTy() || LayoutTy->isFloatingPointTy());
+ ElemSize = LayoutTy->getScalarSizeInBits() / 8;
+ Align = ElemSize;
+ }
+ if (PackoffsetAttr) {
+ ElemOffset = PackoffsetAttr->getOffsetInBytes();
+ } else {
+ ElemOffset = llvm::alignTo(EndOffset, Align);
+ if (ElemOffset + ElemSize > NextRowOffset)
+ ElemOffset = NextRowOffset;
+ }
}
- 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));
+ // Update end offset of the buffer/struct layout; do not update it if
+ // the provided EndOffset is already bigger than the new one (which may happen
+ // with packoffset annotations)
+ unsigned NewEndOffset =
+ ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
+ EndOffset = std::max<size_t>(EndOffset, NewEndOffset);
+
+ // create metadata constan with the offset and stride and add to list
+ LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), ElemOffset));
+ if (ArrayStride)
+ LayoutItems.push_back(
+ getConstIntMetadata(CGM.getLLVMContext(), ArrayStride));
}
-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)) {
+void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
+ llvm::GlobalVariable *BufGV) {
+ llvm::StructType *LayoutStruct = cast<llvm::StructType>(
+ cast<llvm::TargetExtType>(BufGV->getValueType())->getTypeParameter(0));
+
+ LLVMContext &Ctx = CGM.getLLVMContext();
+
+ SmallVector<llvm::Metadata *> BufGlobals;
+ BufGlobals.push_back(ValueAsMetadata::get(BufGV));
+
+ SmallVector<llvm::Metadata *> LayoutItems;
+ LayoutItems.push_back(MDString::get(Ctx, LayoutStruct->getName()));
+
+ size_t BufferSize = 0;
+ size_t BufferSizeIndex = LayoutItems.size();
+ LayoutItems.push_back(nullptr); // reserve one slot for the buffer size
+
+ bool UsePackoffset = BufDecl->hasPackoffset();
+
+ 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)) {
+ continue;
+ if (isa<FunctionDecl>(D)) {
// A function within an cbuffer is effectively a top-level function,
// as it only refers to globally scoped declarations.
- CGM.EmitTopLevelDecl(it);
+ CGM.EmitTopLevelDecl(D);
+ continue;
+ }
+ if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ QualType VDTy = VD->getType();
+ if (VDTy.getAddressSpace() != LangAS::hlsl_constant) {
+ if (VD->getStorageClass() == SC_Static ||
+ isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) {
+ // Emit static variables and resource classes inside cbuffer as
+ // regular globals
+ CGM.EmitGlobal(VD);
+ }
+ // 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
+ continue;
+ }
+
+ assert(ElemIt != LayoutStruct->element_end() &&
+ "number of elements in layout struct does not match");
+ llvm::Type *LayoutType = *ElemIt++;
+
+ assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType ||
+ (LayoutType->isStructTy() &&
+ cast<llvm::StructType>(LayoutType)
+ ->getName()
+ .starts_with(("struct.__cblayout_" +
+ VDTy->getAsCXXRecordDecl()->getName())
+ .str()))) &&
+ "layout type does not match the converted element type");
+
+ // handle any resources declarations inside the struct
+ if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType())
+ // FIXME: handle resources in cbuffer structs
+ llvm_unreachable("resources in cbuffer are not supported yet");
+
+ GlobalVariable *ElemGV =
+ cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType));
+ BufGlobals.push_back(ValueAsMetadata::get(ElemGV));
+
+ assert(((UsePackoffset && VD->hasAttr<HLSLPackOffsetAttr>()) ||
+ !UsePackoffset) &&
+ "expected packoffset attribute on every declaration");
+
+ addLayoutInfoForBufferElement(
+ BufferSize, LayoutItems, LayoutType,
+ UsePackoffset ? VD->getAttr<HLSLPackOffsetAttr>() : nullptr);
}
}
+ assert(ElemIt == LayoutStruct->element_end() &&
+ "number of elements in layout struct does not match");
+
+ // add buffer global and a list of its constants to metadata
+ MDNode *BufMDNode = MDNode::get(CGM.getLLVMContext(), BufGlobals);
+ CGM.getModule().getOrInsertNamedMetadata("hlsl.cbs")->addOperand(BufMDNode);
+
+ // set the size of the buffer
+ LayoutItems[BufferSizeIndex] = getConstIntMetadata(Ctx, BufferSize);
+
+ // add buffer layout to metadata
+ MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems);
+ CGM.getModule()
+ .getOrInsertNamedMetadata("hlsl.cblayouts")
+ ->addOperand(LayoutMDNode);
}
-void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) {
- Buffers.emplace_back(Buffer(D));
- addBufferDecls(D, Buffers.back());
+// Creates resource handle type for the HLSL buffer
+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());
+}
+
+// Creates temporary global variables for all declarations within the constant
+// buffer context, creates a global variable for the constant buffer and adds
+// it to the module.
+// All uses of the temporary constant globals will be replaced with buffer
+// access intrinsic resource.getpointer in CGHLSLRuntime::finishCodeGen.
+// Later on in DXILResourceAccess pass these will be transtaled
+// to dx.op.cbufferLoadLegacy instructions.
+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);
+
+ // ignore empty constant buffer
+ if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty())
+ return;
+
+ // Create global variable for the buffer
+ llvm::Module &M = CGM.getModule();
+ llvm::TargetExtType *TargetTy =
+ cast<llvm::TargetExtType>(convertHLSLSpecificType(ResHandleTy));
+ llvm::GlobalVariable *BufGV =
+ new GlobalVariable(TargetTy, /*isConstant*/ true,
+ GlobalValue::LinkageTypes::ExternalLinkage, nullptr,
+ llvm::formatv("{0}{1}", BufDecl->getName(),
+ BufDecl->isCBuffer() ? ".cb" : ".tb"),
+ GlobalValue::NotThreadLocal);
+ M.insertGlobalVariable(BufGV);
+
+ // Add globals for buffer elements and create metadata node for the buffer
+ emitBufferGlobalsAndMetadata(BufDecl, BufGV);
+
+ // Add cbuffer resource initialization
+ const HLSLResourceBindingAttr *RBA =
+ BufDecl->getAttr<HLSLResourceBindingAttr>();
+ // FIXME: handle implicit binding if no binding attribute is found
+ if (RBA)
+ createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(),
+ RBA->getSpaceNumber());
}
void CGHLSLRuntime::finishCodeGen() {
@@ -173,28 +370,8 @@ void CGHLSLRuntime::finishCodeGen() {
generateGlobalCtorDtorCalls();
if (CGM.getCodeGenOpts().OptimizationLevel == 0)
addDisableOptimizations(M);
-
- 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,
@@ -521,21 +698,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 =
@@ -544,20 +715,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 */
@@ -569,9 +735,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,
@@ -598,8 +764,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 032b2dee82f211..a6e3c78f522851 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,25 +47,31 @@
} \
}
+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 HLSLBufferDecl;
class HLSLResourceBindingAttr;
class Type;
class DeclContext;
+class HLSLPackOffsetAttr;
class FunctionDecl;
namespace CodeGen {
class CodeGenModule;
+class CGBuilderTy;
class CGHLSLRuntime {
public:
@@ -124,16 +131,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;
@@ -167,10 +164,18 @@ 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);
+ void addLayoutInfoForBufferElement(
+ size_t &EndOffset, SmallVector<llvm::Metadata *> &LayoutItems,
+ llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr = nullptr);
+
+ size_t getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy);
+
llvm::Triple::ArchType getArch();
- llvm::SmallVector<Buffer> Buffers;
+
+ // sizes of structs that in constant buffer layout
+ llvm::DenseMap<llvm::StructType *, size_t> StructSizesForBuffer;
};
} // namespace CodeGen
diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp
index 7935f7ae370047..33bddc56d83f24 100644
--- a/clang/lib/CodeGen/Targets/DirectX.cpp
+++ b/clang/lib/CodeGen/Targets/DirectX.cpp
@@ -44,6 +44,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
return nullptr;
// convert element type
+ // llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy);
llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
llvm::StringRef TypeName =
@@ -56,9 +57,13 @@ 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 StructTy = ResType->getContainedType();
+ if (StructTy.isNull())
+ return nullptr;
+ llvm::Type *Ty = CGM.getTypes().ConvertType(StructTy);
+ return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {Ty});
+ }
case llvm::dxil::ResourceClass::Sampler:
llvm_unreachable("dx.Sampler handles are not implemented yet");
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index b71dd2b273a1c6..2807cc773320b2 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->setHasPackoffset(IsValid);
}
// Returns true if the array has a zero size = if any of the dimensions is 0
diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl
deleted file mode 100644
index 825e7b8161a601..00000000000000
--- 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 13c401d4283313..00000000000000
--- 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 00000000000000..99f16ea62cdcbc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer.hlsl
@@ -0,0 +1,200 @@
+// 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_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }>
+// CHECK: %struct.__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: %struct.__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: %struct.__cblayout_CBStructs = type { %struct.A, %struct.B, %struct.C, [5 x %struct.A], %struct.__cblayout_D, half, %struct.B, <3 x i16> }
+// CHECK: %struct.A = type { <2 x float> }
+// CHECK: %struct.C = type { i32, %struct.A }
+// CHECK: %struct.__cblayout_D = type { [2 x [3 x %struct.B]] }
+// CHECK: %struct.B = type { %struct.A, <3 x i16> }
+
+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", %struct.__cblayout_CBScalars)
+// 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 s bool vectors after llvm-project/llvm#91639 is added
+}
+
+// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBVectors)
+// 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", %struct.__cblayout_CBArrays)
+// 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", %struct.__cblayout_CBStructs)
+// CHECK: @a = external addrspace(2) global %struct.A, align 8
+// CHECK: @b = external addrspace(2) global %struct.B, align 8
+// CHECK: @c = external addrspace(2) global %struct.C, align 8
+// CHECK: @array_of_A = external addrspace(2) global [5 x %struct.A], align 8
+// CHECK: @d = external addrspace(2) global %struct.__cblayout_D, align 8
+// CHECK: @e = external addrspace(2) global half, align 2
+
+cbuffer CBStructs {
+ A a;
+ B b;
+ C c;
+ A array_of_A[5];
+ D d;
+ half e;
+ B f;
+ uint16_t3 g;
+};
+
+struct Test {
+ float a, b;
+};
+
+// CHECK: @CBMix.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBMix)
+// CHECK: @test = external addrspace(2) global [2 x %struct.Test], 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: @s = external addrspace(2) global %struct.anon, align 4
+// CHECK: @dd = external addrspace(2) global double, align 8
+// CHECK: @f4 = external addrspace(2) global float, align 4
+// CHECK: @dv = external addrspace(2) global <1 x double>, align 8
+// CHECK: @uv = external addrspace(2) global i16, align 2
+
+cbuffer CBMix {
+ Test test[2];
+ float f1;
+ float2 f2[3][2];
+ float f3;
+ struct { float c; } s;
+ double dd;
+ float f4;
+ vector<double,1> dv;
+ uint16_t uv;
+};
+
+// CHECK: efine internal void @_init_resource_CBScalars.cb()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBScalars)
+// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBScalarsst(i32 5, i32 1, i32 1, i32 0, i1 false)
+// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBScalars) %[[HANDLE1]], ptr @CBScalars.cb, align 4
+
+// CHECK: define internal void @_init_resource_CBArrays.cb()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBArrays)
+// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBArraysst(i32 0, i32 2, i32 1, i32 0, i1 false)
+// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBArrays) %[[HANDLE2]], ptr @CBArrays.cb, align 4
+
+RWBuffer<float> Buf;
+
+[numthreads(4,1,1)]
+void main() {
+ //Buf[0] = a1 + b1.z + c1[2] + a.f1.y;
+ Buf[0] = a.f1.y;
+}
+
+// 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: !hlsl.cblayouts = !{![[CBSCALARS_LAYOUT:[0-9]+]], ![[CBVECTORS_LAYOUT:[0-9]+]], ![[CBARRAYS_LAYOUT:[0-9]+]], ![[A_LAYOUT:[0-9]+]],
+// CHECK-SAME: ![[B_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]], ![[D_LAYOUT:[0-9]+]], ![[CBSTRUCTS_LAYOUT:[0-9]+]], ![[TEST_LAYOUT:[0-9]+]],
+// CHECK-SAME: ![[ANON_LAYOUT:[0-9]+]], ![[CBMIX_LAYOUT:[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, ptr addrspace(2) @g}
+
+// 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) @s, ptr addrspace(2) @dd, ptr addrspace(2) @f4, ptr addrspace(2) @dv, ptr addrspace(2) @uv}
+
+// CHECK: ![[CBSCALARS_LAYOUT]] = !{!"struct.__cblayout_CBScalars", i32 56, i32 0, i32 8, i32 16, i32 24, i32 32, i32 36, i32 40, i32 48}
+// CHECK: ![[CBVECTORS_LAYOUT]] = !{!"struct.__cblayout_CBVectors", i32 136, i32 0, i32 16, i32 40, i32 48, i32 80, i32 96, i32 112}
+
+// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 16, i32 48, i32 32, i32 112, i32 16, i32 176, i32 16,
+// CHECK-SAME: i32 224, i32 16, i32 608, i32 16, i32 624, i32 16, i32 656, i32 16}
+
+// CHECK: ![[A_LAYOUT]] = !{!"struct.A", i32 8, i32 0}
+// CHECK: ![[B_LAYOUT]] = !{!"struct.B", i32 14, i32 0, i32 8}
+// CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16}
+// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0, i32 16}
+// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 16, i32 144, i32 238, i32 240, i32 256}
+
+// CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4}
+// CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0}
+// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 16, i32 24, i32 32, i32 16, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160}
diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
new file mode 100644
index 00000000000000..aa5659af414efa
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl
@@ -0,0 +1,63 @@
+// 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: %"struct.n0::n1::__cblayout_A" = type { float }
+// CHECK: %"struct.n0::__cblayout_B" = type { float }
+// CHECK: %"struct.n0::n2::__cblayout_C" = type { float, %"struct.n0::Foo" }
+// CHECK: %"struct.n0::Foo" = type { float }
+
+// CHECK: @A.cb = external constant target("dx.CBuffer", %"struct.n0::n1::__cblayout_A")
+// CHECK: @_ZN2n02n11aE = external addrspace(2) global float, align 4
+
+// CHECK: @B.cb = external constant target("dx.CBuffer", %"struct.n0::__cblayout_B")
+// CHECK: @_ZN2n01aE = external addrspace(2) global float, align 4
+
+// CHECK: @C.cb = external constant target("dx.CBuffer", %"struct.n0::n2::__cblayout_C")
+// CHECK: @_ZN2n02n21aE = external addrspace(2) global float, align 4
+// CHECK: @_ZN2n02n21bE = external addrspace(2) global %"struct.n0::Foo", 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: !hlsl.cblayouts = !{![[A_LAYOUT:[0-9]+]], ![[B_LAYOUT:[0-9]+]], ![[FOO_LAYOUT:[0-9]+]], ![[C_LAYOUT:[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}
+
+// CHECK: ![[A_LAYOUT]] = !{!"struct.n0::n1::__cblayout_A", i32 4, i32 0}
+// CHECK: ![[B_LAYOUT]] = !{!"struct.n0::__cblayout_B", i32 4, i32 0}
+// CHECK: ![[FOO_LAYOUT]] = !{!"struct.n0::Foo", i32 4, i32 0}
+// CHECK: ![[C_LAYOUT]] = !{!"struct.n0::n2::__cblayout_C", i32 20, i32 0, i32 16}
diff --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
new file mode 100644
index 00000000000000..f5b5cba6197b39
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
@@ -0,0 +1,40 @@
+// 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: %struct.__cblayout_CB = type <{ float, double, <2 x i32> }>
+
+// CHECK: @CB.cb = external constant target("dx.CBuffer", %struct.__cblayout_CB)
+// 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", %struct.__cblayout_CB) @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBst(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: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]}
+
+// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
+// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_CB", i32 176, i32 16, i32 168, i32 88}
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 00000000000000..8994640d5b3112
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
+// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+// CHECK: %struct.__cblayout_A = type { float }
+
+// CHECK: @A.cb = external constant target("dx.CBuffer", %struct.__cblayout_A)
+// 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: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]}
+
+// CHECK: ![[CB]] = !{ptr @A.cb, ptr addrspace(2) @a}
+// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_A", i32 4, i32 0}
diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl
index bfa7896bd98114..57e8cc29572b1f 100644
--- a/clang/test/CodeGenHLSL/resource-bindings.hlsl
+++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl
@@ -2,14 +2,17 @@
// CHECK: define internal void @_init_resource_U0S0()
// CHECK: %U0S0_h = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %U0S0_h, ptr @U0S0, align 4
RWBuffer<float4> U0S0 : register(u0);
// CHECK: define internal void @_init_resource_U5S3()
// CHECK: %U5S3_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %U5S3_h, ptr @U5S3, align 4
RWBuffer<float> U5S3 : register(u5, space3);
// CHECK: define internal void @_init_resource_T2S2()
// CHECK: %T2S2_h = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", i32, 0, 0) %T2S2_h, ptr @T2S2, align 4
StructuredBuffer<int> T2S2 : register(t2, space2);
struct S {
float4 f;
@@ -18,6 +21,7 @@ struct S {
// CHECK: define internal void @_init_resource_T3S0()
// CHECK: %T3S0_h = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) %T3S0_h, ptr @T3S0, align 4
StructuredBuffer<S> T3S0 : register(t3);
// CHECK: define void @main()
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 25f51cce2017d2..00000000000000
--- 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();
-}
>From 56beb45a133be18b7e6649cc0a4af852bbed3db7 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 28 Jan 2025 16:06:03 -0800
Subject: [PATCH 2/4] remove stride
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 32 +++++++++++++++--------------
clang/lib/CodeGen/CGHLSLRuntime.h | 7 ++++---
clang/test/CodeGenHLSL/cbuffer.hlsl | 9 ++++----
3 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index d3d9d426643596..128ca86f66b5aa 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -130,8 +130,11 @@ CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) {
size_t StructSize = 0;
LayoutItems.push_back(nullptr); // reserve one slot for the buffer size
- for (llvm::Type *ElTy : StructTy->elements())
- addLayoutInfoForBufferElement(StructSize, LayoutItems, ElTy);
+ for (llvm::Type *ElTy : StructTy->elements()) {
+ size_t Offset = calculateBufferElementOffset(ElTy, &StructSize);
+ // create metadata constant with the element start offset
+ LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset));
+ }
// set the size of the buffer
LayoutItems[1] = getConstIntMetadata(Ctx, StructSize);
@@ -147,16 +150,16 @@ CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) {
return StructSize;
}
-void CGHLSLRuntime::addLayoutInfoForBufferElement(
- size_t &EndOffset, SmallVector<llvm::Metadata *> &LayoutItems,
- llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr) {
+size_t CGHLSLRuntime::calculateBufferElementOffset(
+ llvm::Type *LayoutTy, size_t *LayoutEndOffset,
+ HLSLPackOffsetAttr *PackoffsetAttr) {
- // calculate element offset and size; for arrays also calculate array
- // element count and stride
+ // calculate element offset and size
size_t ElemOffset = 0;
size_t ElemSize = 0;
size_t ArrayCount = 1;
size_t ArrayStride = 0;
+ size_t EndOffset = *LayoutEndOffset;
size_t NextRowOffset = llvm::alignTo(EndOffset, 16U);
if (LayoutTy->isArrayTy()) {
@@ -205,13 +208,9 @@ void CGHLSLRuntime::addLayoutInfoForBufferElement(
// with packoffset annotations)
unsigned NewEndOffset =
ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
- EndOffset = std::max<size_t>(EndOffset, NewEndOffset);
+ *LayoutEndOffset = std::max<size_t>(EndOffset, NewEndOffset);
- // create metadata constan with the offset and stride and add to list
- LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), ElemOffset));
- if (ArrayStride)
- LayoutItems.push_back(
- getConstIntMetadata(CGM.getLLVMContext(), ArrayStride));
+ return ElemOffset;
}
void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
@@ -284,9 +283,12 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
!UsePackoffset) &&
"expected packoffset attribute on every declaration");
- addLayoutInfoForBufferElement(
- BufferSize, LayoutItems, LayoutType,
+ size_t Offset = calculateBufferElementOffset(
+ LayoutType, &BufferSize,
UsePackoffset ? VD->getAttr<HLSLPackOffsetAttr>() : nullptr);
+
+ // create metadata constant with the element start offset
+ LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset));
}
}
assert(ElemIt == LayoutStruct->element_end() &&
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index a6e3c78f522851..d1f678863d4fb7 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -166,9 +166,10 @@ class CGHLSLRuntime {
BufferResBinding &Binding);
void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *BufGV);
- void addLayoutInfoForBufferElement(
- size_t &EndOffset, SmallVector<llvm::Metadata *> &LayoutItems,
- llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr = nullptr);
+
+ size_t
+ calculateBufferElementOffset(llvm::Type *LayoutTy, size_t *LayoutEndOffset,
+ HLSLPackOffsetAttr *PackoffsetAttr = nullptr);
size_t getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy);
diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl
index 99f16ea62cdcbc..e600d25c4e19cf 100644
--- a/clang/test/CodeGenHLSL/cbuffer.hlsl
+++ b/clang/test/CodeGenHLSL/cbuffer.hlsl
@@ -186,15 +186,14 @@ void main() {
// CHECK: ![[CBSCALARS_LAYOUT]] = !{!"struct.__cblayout_CBScalars", i32 56, i32 0, i32 8, i32 16, i32 24, i32 32, i32 36, i32 40, i32 48}
// CHECK: ![[CBVECTORS_LAYOUT]] = !{!"struct.__cblayout_CBVectors", i32 136, i32 0, i32 16, i32 40, i32 48, i32 80, i32 96, i32 112}
-// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 16, i32 48, i32 32, i32 112, i32 16, i32 176, i32 16,
-// CHECK-SAME: i32 224, i32 16, i32 608, i32 16, i32 624, i32 16, i32 656, i32 16}
+// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 48, i32 112, i32 176, i32 224, i32 608, i32 624, i32 656}
// CHECK: ![[A_LAYOUT]] = !{!"struct.A", i32 8, i32 0}
// CHECK: ![[B_LAYOUT]] = !{!"struct.B", i32 14, i32 0, i32 8}
// CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16}
-// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0, i32 16}
-// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 16, i32 144, i32 238, i32 240, i32 256}
+// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0}
+// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 144, i32 238, i32 240, i32 256}
// CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4}
// CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0}
-// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 16, i32 24, i32 32, i32 16, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160}
+// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 24, i32 32, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160}
>From 749e88e87f36b8b4f2a8b5a4ddf02230a4be9600 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 28 Jan 2025 22:02:24 -0800
Subject: [PATCH 3/4] cleanup, update comments
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 188 +++++++++++++-------------
clang/lib/CodeGen/CGHLSLRuntime.h | 4 +-
clang/lib/CodeGen/Targets/DirectX.cpp | 1 -
clang/test/CodeGenHLSL/cbuffer.hlsl | 2 +-
4 files changed, 100 insertions(+), 95 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 128ca86f66b5aa..52247173b69901 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -89,8 +89,7 @@ static bool isResourceRecordType(const clang::Type *Ty) {
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
}
-// Returns true if the type is an HLSL resource class or an array of
-// HLSL resource classes
+// 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();
@@ -113,48 +112,56 @@ static unsigned getScalarOrVectorSize(llvm::Type *Ty) {
return Ty->getScalarSizeInBits() / 8;
}
+// Returns size of a struct in constant buffer layout. The sizes are cached
+// in StructSizesForBuffer map. The map is also an indicator if a layout
+// metadata for this struct has been added to the module.
+// If the struct type is not in the map, this method will calculate the struct
+// layout, add a metadata node describing it to the module, and add the struct
+// size to the map.
size_t
CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) {
- assert(StructTy->isStructTy());
-
// check if we already have a side for this struct
auto SizeIt = StructSizesForBuffer.find(StructTy);
if (SizeIt != StructSizesForBuffer.end())
return SizeIt->getSecond();
- // if not, calculate the struct layout and add it to metadata
+ // if not, calculate the struct layout and create a metadata node
LLVMContext &Ctx = CGM.getLLVMContext();
SmallVector<llvm::Metadata *> LayoutItems;
+
+ // start metadata list with a struct name and reserve one slot for its size
LayoutItems.push_back(MDString::get(Ctx, StructTy->getName()));
+ LayoutItems.push_back(nullptr);
+ // add element offsets
size_t StructSize = 0;
- LayoutItems.push_back(nullptr); // reserve one slot for the buffer size
-
for (llvm::Type *ElTy : StructTy->elements()) {
size_t Offset = calculateBufferElementOffset(ElTy, &StructSize);
- // create metadata constant with the element start offset
LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset));
}
-
- // set the size of the buffer
+ // set the size of the buffer to the reserved slot
LayoutItems[1] = getConstIntMetadata(Ctx, StructSize);
- // add the struct layout info to metadata
- MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems);
+ // add the struct layout to metadata
CGM.getModule()
.getOrInsertNamedMetadata("hlsl.cblayouts")
- ->addOperand(LayoutMDNode);
+ ->addOperand(MDNode::get(CGM.getLLVMContext(), LayoutItems));
// add struct size to list and return it
StructSizesForBuffer[StructTy] = StructSize;
return StructSize;
}
+// Calculates offset of a single element in constant buffer layout.
+// The provided LayoutEndOffset marks the end of the layout so far (end offset
+// of the buffer or struct). After the element offset calculations are done it
+// will be updated the new end of layout value.
+// If the PackoffsetAttrs is not nullptr the offset will be based on the
+// packoffset annotation.
size_t CGHLSLRuntime::calculateBufferElementOffset(
llvm::Type *LayoutTy, size_t *LayoutEndOffset,
HLSLPackOffsetAttr *PackoffsetAttr) {
- // calculate element offset and size
size_t ElemOffset = 0;
size_t ElemSize = 0;
size_t ArrayCount = 1;
@@ -198,14 +205,15 @@ size_t CGHLSLRuntime::calculateBufferElementOffset(
ElemOffset = PackoffsetAttr->getOffsetInBytes();
} 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 buffer/struct layout; do not update it if
- // the provided EndOffset is already bigger than the new one (which may happen
- // with packoffset annotations)
+ // the provided EndOffset is already bigger than the new one value
+ // (which may happen with packoffset annotations)
unsigned NewEndOffset =
ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
*LayoutEndOffset = std::max<size_t>(EndOffset, NewEndOffset);
@@ -213,102 +221,106 @@ size_t CGHLSLRuntime::calculateBufferElementOffset(
return ElemOffset;
}
+// Emits constant global variables for buffer declarations, creates metadata
+// linking the constant globals with the buffer. Also calculates the buffer
+// layout and creates metadata node describing it.
void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *BufGV) {
+ LLVMContext &Ctx = CGM.getLLVMContext();
llvm::StructType *LayoutStruct = cast<llvm::StructType>(
cast<llvm::TargetExtType>(BufGV->getValueType())->getTypeParameter(0));
- LLVMContext &Ctx = CGM.getLLVMContext();
-
+ // Start metadata list associating the buffer global variable with its
+ // constatns
SmallVector<llvm::Metadata *> BufGlobals;
BufGlobals.push_back(ValueAsMetadata::get(BufGV));
+ // Start layout metadata list with a struct name and reserve one slot for
+ // the buffer size
SmallVector<llvm::Metadata *> LayoutItems;
LayoutItems.push_back(MDString::get(Ctx, LayoutStruct->getName()));
+ LayoutItems.push_back(nullptr);
size_t BufferSize = 0;
- size_t BufferSizeIndex = LayoutItems.size();
- LayoutItems.push_back(nullptr); // reserve one slot for the buffer size
-
bool UsePackoffset = BufDecl->hasPackoffset();
-
const auto *ElemIt = LayoutStruct->element_begin();
for (Decl *D : BufDecl->decls()) {
if (isa<CXXRecordDecl, EmptyDecl>(D))
// Nothing to do for this declaration.
continue;
if (isa<FunctionDecl>(D)) {
- // A function within an cbuffer is effectively a top-level function,
- // as it only refers to globally scoped declarations.
+ // A function within an cbuffer is effectively a top-level function.
CGM.EmitTopLevelDecl(D);
continue;
}
- if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
- QualType VDTy = VD->getType();
- if (VDTy.getAddressSpace() != LangAS::hlsl_constant) {
- if (VD->getStorageClass() == SC_Static ||
- isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) {
- // Emit static variables and resource classes inside cbuffer as
- // regular globals
- CGM.EmitGlobal(VD);
- }
- // 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
- continue;
- }
+ VarDecl *VD = dyn_cast<VarDecl>(D);
+ if (!VD)
+ continue;
- assert(ElemIt != LayoutStruct->element_end() &&
- "number of elements in layout struct does not match");
- llvm::Type *LayoutType = *ElemIt++;
-
- assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType ||
- (LayoutType->isStructTy() &&
- cast<llvm::StructType>(LayoutType)
- ->getName()
- .starts_with(("struct.__cblayout_" +
- VDTy->getAsCXXRecordDecl()->getName())
- .str()))) &&
- "layout type does not match the converted element type");
-
- // handle any resources declarations inside the struct
- if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType())
- // FIXME: handle resources in cbuffer structs
- llvm_unreachable("resources in cbuffer are not supported yet");
-
- GlobalVariable *ElemGV =
- cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType));
- BufGlobals.push_back(ValueAsMetadata::get(ElemGV));
-
- assert(((UsePackoffset && VD->hasAttr<HLSLPackOffsetAttr>()) ||
- !UsePackoffset) &&
- "expected packoffset attribute on every declaration");
-
- size_t Offset = calculateBufferElementOffset(
- LayoutType, &BufferSize,
- UsePackoffset ? VD->getAttr<HLSLPackOffsetAttr>() : nullptr);
-
- // create metadata constant with the element start offset
- LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset));
+ 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);
+ }
+ // 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
+ continue;
}
+
+ assert(ElemIt != LayoutStruct->element_end() &&
+ "number of elements in layout struct does not match");
+ llvm::Type *LayoutType = *ElemIt++;
+
+ // Make sure the type of the VarDecl type matches the type of the layout
+ // struct element, or that it is a layout struct with the same name
+ assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType ||
+ (LayoutType->isStructTy() &&
+ cast<llvm::StructType>(LayoutType)
+ ->getName()
+ .starts_with(("struct.__cblayout_" +
+ VDTy->getAsCXXRecordDecl()->getName())
+ .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");
+
+ // create global variable for the constant and to metadata list
+ GlobalVariable *ElemGV =
+ cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType));
+ BufGlobals.push_back(ValueAsMetadata::get(ElemGV));
+
+ // get offset of the global and and to metadata list
+ assert(((UsePackoffset && VD->hasAttr<HLSLPackOffsetAttr>()) ||
+ !UsePackoffset) &&
+ "expected packoffset attribute on every declaration");
+ size_t Offset = calculateBufferElementOffset(
+ LayoutType, &BufferSize,
+ UsePackoffset ? VD->getAttr<HLSLPackOffsetAttr>() : nullptr);
+ LayoutItems.push_back(getConstIntMetadata(Ctx, Offset));
}
assert(ElemIt == LayoutStruct->element_end() &&
"number of elements in layout struct does not match");
-
- // add buffer global and a list of its constants to metadata
- MDNode *BufMDNode = MDNode::get(CGM.getLLVMContext(), BufGlobals);
- CGM.getModule().getOrInsertNamedMetadata("hlsl.cbs")->addOperand(BufMDNode);
-
// set the size of the buffer
- LayoutItems[BufferSizeIndex] = getConstIntMetadata(Ctx, BufferSize);
+ LayoutItems[1] = getConstIntMetadata(Ctx, BufferSize);
+
+ // add buffer metadata to the module
+ CGM.getModule()
+ .getOrInsertNamedMetadata("hlsl.cbs")
+ ->addOperand(MDNode::get(Ctx, BufGlobals));
- // add buffer layout to metadata
- MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems);
CGM.getModule()
.getOrInsertNamedMetadata("hlsl.cblayouts")
- ->addOperand(LayoutMDNode);
+ ->addOperand(MDNode::get(Ctx, LayoutItems));
}
-// Creates resource handle type for the HLSL buffer
+// Creates resource handle type for the HLSL buffer declaration
static const clang::HLSLAttributedResourceType *
createBufferHandleType(const HLSLBufferDecl *BufDecl) {
ASTContext &AST = BufDecl->getASTContext();
@@ -319,26 +331,20 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
return cast<HLSLAttributedResourceType>(QT.getTypePtr());
}
-// Creates temporary global variables for all declarations within the constant
-// buffer context, creates a global variable for the constant buffer and adds
-// it to the module.
-// All uses of the temporary constant globals will be replaced with buffer
-// access intrinsic resource.getpointer in CGHLSLRuntime::finishCodeGen.
-// Later on in DXILResourceAccess pass these will be transtaled
-// to dx.op.cbufferLoadLegacy instructions.
+// 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
+ // create resource handle type for the buffer
const clang::HLSLAttributedResourceType *ResHandleTy =
createBufferHandleType(BufDecl);
- // ignore empty constant buffer
+ // empty constant buffer is ignored
if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty())
return;
- // Create global variable for the buffer
+ // create global variable for the constant buffer
llvm::Module &M = CGM.getModule();
llvm::TargetExtType *TargetTy =
cast<llvm::TargetExtType>(convertHLSLSpecificType(ResHandleTy));
@@ -350,10 +356,10 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
GlobalValue::NotThreadLocal);
M.insertGlobalVariable(BufGV);
- // Add globals for buffer elements and create metadata node for the buffer
+ // Add globals for constant buffer elements and create metadata nodes
emitBufferGlobalsAndMetadata(BufDecl, BufGV);
- // Add cbuffer resource initialization
+ // Resource initialization
const HLSLResourceBindingAttr *RBA =
BufDecl->getAttr<HLSLResourceBindingAttr>();
// FIXME: handle implicit binding if no binding attribute is found
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index d1f678863d4fb7..8658cb8653fb46 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -71,7 +71,6 @@ class FunctionDecl;
namespace CodeGen {
class CodeGenModule;
-class CGBuilderTy;
class CGHLSLRuntime {
public:
@@ -175,7 +174,8 @@ class CGHLSLRuntime {
llvm::Triple::ArchType getArch();
- // sizes of structs that in constant buffer layout
+ // Sizes of structs in constant buffer layout. Structs in the map
+ // had their layout calculated and added to the module as metadata.
llvm::DenseMap<llvm::StructType *, size_t> StructSizesForBuffer;
};
diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp
index 33bddc56d83f24..ec7fa342d58468 100644
--- a/clang/lib/CodeGen/Targets/DirectX.cpp
+++ b/clang/lib/CodeGen/Targets/DirectX.cpp
@@ -44,7 +44,6 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
return nullptr;
// convert element type
- // llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy);
llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
llvm::StringRef TypeName =
diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl
index e600d25c4e19cf..9a3f632e859c76 100644
--- a/clang/test/CodeGenHLSL/cbuffer.hlsl
+++ b/clang/test/CodeGenHLSL/cbuffer.hlsl
@@ -164,6 +164,7 @@ void main() {
// 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: !hlsl.cblayouts = !{![[CBSCALARS_LAYOUT:[0-9]+]], ![[CBVECTORS_LAYOUT:[0-9]+]], ![[CBARRAYS_LAYOUT:[0-9]+]], ![[A_LAYOUT:[0-9]+]],
// CHECK-SAME: ![[B_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]], ![[D_LAYOUT:[0-9]+]], ![[CBSTRUCTS_LAYOUT:[0-9]+]], ![[TEST_LAYOUT:[0-9]+]],
// CHECK-SAME: ![[ANON_LAYOUT:[0-9]+]], ![[CBMIX_LAYOUT:[0-9]+]]}
@@ -193,7 +194,6 @@ void main() {
// CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16}
// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0}
// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 144, i32 238, i32 240, i32 256}
-
// CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4}
// CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0}
// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 24, i32 32, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160}
>From 66ae78472080bb2b9de4b99ff63b5219f14c75c8 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 4 Feb 2025 17:58:43 -0800
Subject: [PATCH 4/4] Add LayoutStruct to HLSLBufferDecl; add skipping of
implicit initializers
---
clang/include/clang/AST/Decl.h | 5 ++++-
clang/lib/AST/Decl.cpp | 15 +++++----------
clang/lib/Sema/SemaDecl.cpp | 6 ++++++
clang/lib/Sema/SemaHLSL.cpp | 2 +-
4 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index e1c7e3817699ce..05e56978977f2b 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5035,6 +5035,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
/// HasValidPackoffset - Whether the buffer has valid packoffset annotations
// on all declarations
bool HasPackoffset;
+ // LayoutStruct - Layout struct for the buffer
+ CXXRecordDecl *LayoutStruct;
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
@@ -5057,7 +5059,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
bool isCBuffer() const { return IsCBuffer; }
void setHasPackoffset(bool PO) { HasPackoffset = PO; }
bool hasPackoffset() const { return HasPackoffset; }
- const CXXRecordDecl *getLayoutStruct() const;
+ 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/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index fa7d03354a9937..4c5201d0ba2ee9 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5717,7 +5717,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), HasPackoffset(false) {}
+ IsCBuffer(CBuffer), HasPackoffset(false), LayoutStruct(nullptr) {}
HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
DeclContext *LexicalParent, bool CBuffer,
@@ -5747,15 +5747,10 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
SourceLocation(), SourceLocation());
}
-const CXXRecordDecl *HLSLBufferDecl::getLayoutStruct() const {
- // Layout struct is the last decl in the HLSLBufferDecl.
- if (CXXRecordDecl *RD = llvm::dyn_cast_or_null<CXXRecordDecl>(LastDecl)) {
- assert(RD->getName().starts_with(
- ("__cblayout_" + getIdentifier()->getName()).str()) &&
- "expected buffer layout struct");
- return RD;
- }
- llvm_unreachable("HLSL buffer is missing a layout struct");
+void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
+ assert(LayoutStruct == nullptr && "layout struct has already been set");
+ LayoutStruct = LS;
+ addDecl(LS);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7c70150871da90..5497c30ed09ffd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14184,6 +14184,12 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
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 2807cc773320b2..d80e605055ad72 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -502,7 +502,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
}
}
LS->completeDefinition();
- BufDecl->addDecl(LS);
+ BufDecl->addLayoutStruct(LS);
}
// Handle end of cbuffer/tbuffer declaration
More information about the llvm-branch-commits
mailing list