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 <heko...@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 16403774e72b31c..e1c7e3817699ce0 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 1d9743520654ebd..c3ff7ebd88516c7 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 beb5fcaefac5353..fa7d03354a99379 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 2ce54cc3c52efa0..d3d9d426643596d 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 032b2dee82f2114..a6e3c78f5228516 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 7935f7ae3700476..33bddc56d83f242 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 b71dd2b273a1c6c..2807cc773320b2d 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 825e7b8161a6014..000000000000000 --- 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 13c401d42833138..000000000000000 --- 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 000000000000000..99f16ea62cdcbc7 --- /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 000000000000000..aa5659af414efa4 --- /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 000000000000000..f5b5cba6197b395 --- /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 000000000000000..8994640d5b31121 --- /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 bfa7896bd981140..57e8cc29572b1fd 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 25f51cce2017d2a..000000000000000 --- 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 <heko...@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 d3d9d426643596d..128ca86f66b5aa6 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 a6e3c78f5228516..d1f678863d4fb7e 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 99f16ea62cdcbc7..e600d25c4e19cf4 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 <heko...@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 128ca86f66b5aa6..52247173b69901b 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 d1f678863d4fb7e..8658cb8653fb462 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 33bddc56d83f242..ec7fa342d58468e 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 e600d25c4e19cf4..9a3f632e859c76c 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 <heko...@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 e1c7e3817699ce0..05e56978977f2b1 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 fa7d03354a99379..4c5201d0ba2ee90 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 7c70150871da903..5497c30ed09ffd2 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 2807cc773320b2d..d80e605055ad725 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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits