Author: Helena Kotas Date: 2025-02-20T10:32:14-08:00 New Revision: 19af8581d51b8144f6d041ae1d948443084d8d0b
URL: https://github.com/llvm/llvm-project/commit/19af8581d51b8144f6d041ae1d948443084d8d0b DIFF: https://github.com/llvm/llvm-project/commit/19af8581d51b8144f6d041ae1d948443084d8d0b.diff LOG: [HLSL] Constant Buffers CodeGen (#124886) Translates `cbuffer` declaration blocks to `target("dx.CBuffer")` type. Creates global variables in `hlsl_constant` address space for all `cbuffer` constant and adds metadata describing which global constant belongs to which constant buffer. For explicit constant buffer layout information an explicit layout type `target("dx.Layout")` is used. This might change in the future. The constant globals are temporary and will be removed in upcoming pass that will translate `load` instructions in the `hlsl_constant` address space to constant buffer load intrinsics calls off a CBV handle (#124630, #112992). See [Constant buffer design doc](https://github.com/llvm/wg-hlsl/pull/94) for more details. Fixes #113514, #106596 Added: clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp clang/lib/CodeGen/HLSLBufferLayoutBuilder.h clang/test/CodeGenHLSL/cbuffer.hlsl clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl Modified: clang/include/clang/AST/Decl.h clang/include/clang/AST/Type.h clang/lib/AST/Decl.cpp clang/lib/CodeGen/CGHLSLRuntime.cpp clang/lib/CodeGen/CGHLSLRuntime.h clang/lib/CodeGen/CMakeLists.txt clang/lib/CodeGen/TargetInfo.h clang/lib/CodeGen/Targets/DirectX.cpp clang/lib/CodeGen/Targets/SPIR.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaHLSL.cpp Removed: clang/test/CodeGenHLSL/cbuf.hlsl clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl ################################################################################ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index f305cbbce4c60..0f96bf0762ca4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5039,6 +5039,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation KwLoc; /// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). bool IsCBuffer; + /// HasValidPackoffset - Whether the buffer has valid packoffset annotations + // on all declarations + bool HasValidPackoffset; + // LayoutStruct - Layout struct for the buffer + CXXRecordDecl *LayoutStruct; HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, @@ -5059,6 +5064,10 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation getRBraceLoc() const { return RBraceLoc; } void setRBraceLoc(SourceLocation L) { RBraceLoc = L; } bool isCBuffer() const { return IsCBuffer; } + void setHasValidPackoffset(bool PO) { HasValidPackoffset = PO; } + bool hasValidPackoffset() const { return HasValidPackoffset; } + const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; } + void addLayoutStruct(CXXRecordDecl *LS); // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1d9743520654e..c3ff7ebd88516 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -6266,8 +6266,8 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t RawBuffer : 1; - Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV, - bool RawBuffer) + Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false, + bool RawBuffer = false) : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {} Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {} diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 610207cf8b9a4..5a3be1690f335 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1747,6 +1747,10 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, } } + // Suppress transparent contexts like export or HLSLBufferDecl context + if (Ctx->isTransparentContext()) + continue; + // Skip non-named contexts such as linkage specifications and ExportDecls. const NamedDecl *ND = dyn_cast<NamedDecl>(Ctx); if (!ND) @@ -5717,7 +5721,7 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation IDLoc, SourceLocation LBrace) : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), - IsCBuffer(CBuffer) {} + IsCBuffer(CBuffer), HasValidPackoffset(false), LayoutStruct(nullptr) {} HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, @@ -5747,6 +5751,12 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, SourceLocation(), SourceLocation()); } +void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) { + assert(LayoutStruct == nullptr && "layout struct has already been set"); + LayoutStruct = LS; + addDecl(LS); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 856d8b1b2948d..547220fb1f1e1 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -17,16 +17,22 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "TargetInfo.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" #include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Alignment.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" using namespace clang; @@ -34,6 +40,9 @@ using namespace CodeGen; using namespace clang::hlsl; using namespace llvm; +static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV, + unsigned Slot, unsigned Space); + namespace { void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { @@ -56,58 +65,17 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey); DXILValMD->addOperand(Val); } -// cbuffer will be translated into global variable in special address space. -// If translate into C, -// cbuffer A { -// float a; -// float b; -// } -// float foo() { return a + b; } -// -// will be translated into -// -// struct A { -// float a; -// float b; -// } cbuffer_A __attribute__((address_space(4))); -// float foo() { return cbuffer_A.a + cbuffer_A.b; } -// -// layoutBuffer will create the struct A type. -// replaceBuffer will replace use of global variable a and b with cbuffer_A.a -// and cbuffer_A.b. -// -void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) { - if (Buf.Constants.empty()) - return; - - std::vector<llvm::Type *> EltTys; - for (auto &Const : Buf.Constants) { - GlobalVariable *GV = Const.first; - Const.second = EltTys.size(); - llvm::Type *Ty = GV->getValueType(); - EltTys.emplace_back(Ty); - } - Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys); -} - -GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) { - // Create global variable for CB. - GlobalVariable *CBGV = new GlobalVariable( - Buf.LayoutStruct, /*isConstant*/ true, - GlobalValue::LinkageTypes::ExternalLinkage, nullptr, - llvm::formatv("{0}{1}", Buf.Name, Buf.IsCBuffer ? ".cb." : ".tb."), - GlobalValue::NotThreadLocal); - - return CBGV; -} } // namespace -llvm::Type *CGHLSLRuntime::convertHLSLSpecificType(const Type *T) { +llvm::Type * +CGHLSLRuntime::convertHLSLSpecificType(const Type *T, + SmallVector<unsigned> *Packoffsets) { assert(T->isHLSLSpecificType() && "Not an HLSL specific type!"); // Check if the target has a specific translation for this type first. - if (llvm::Type *TargetTy = CGM.getTargetCodeGenInfo().getHLSLType(CGM, T)) + if (llvm::Type *TargetTy = + CGM.getTargetCodeGenInfo().getHLSLType(CGM, T, Packoffsets)) return TargetTy; llvm_unreachable("Generic handling of HLSL types is not supported."); @@ -117,48 +85,174 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() { return CGM.getTarget().getTriple().getArch(); } -void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) { - if (D->getStorageClass() == SC_Static) { - // For static inside cbuffer, take as global static. - // Don't add to cbuffer. - CGM.EmitGlobal(D); - return; - } +// Returns true if the type is an HLSL resource class +static bool isResourceRecordType(const clang::Type *Ty) { + return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; +} - auto *GV = cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(D)); - GV->setExternallyInitialized(true); - // Add debug info for constVal. - if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::DebugInfoKind::LimitedDebugInfo) - DI->EmitGlobalVariable(cast<GlobalVariable>(GV), D); - - // FIXME: support packoffset. - // See https://github.com/llvm/llvm-project/issues/57914. - uint32_t Offset = 0; - bool HasUserOffset = false; - - unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX; - CB.Constants.emplace_back(std::make_pair(GV, LowerBound)); +// Returns true if the type is an HLSL resource class or an array of them +static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { + while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) + Ty = CAT->getArrayElementTypeNoTypeQual(); + return isResourceRecordType(Ty); } -void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) { - for (Decl *it : DC->decls()) { - if (auto *ConstDecl = dyn_cast<VarDecl>(it)) { - addConstant(ConstDecl, CB); - } else if (isa<CXXRecordDecl, EmptyDecl>(it)) { +// Emits constant global variables for buffer constants declarations +// and creates metadata linking the constant globals with the buffer global. +void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, + llvm::GlobalVariable *BufGV) { + LLVMContext &Ctx = CGM.getLLVMContext(); + + // get the layout struct from constant buffer target type + llvm::Type *BufType = BufGV->getValueType(); + llvm::Type *BufLayoutType = + cast<llvm::TargetExtType>(BufType)->getTypeParameter(0); + llvm::StructType *LayoutStruct = cast<llvm::StructType>( + cast<llvm::TargetExtType>(BufLayoutType)->getTypeParameter(0)); + + // Start metadata list associating the buffer global variable with its + // constatns + SmallVector<llvm::Metadata *> BufGlobals; + BufGlobals.push_back(ValueAsMetadata::get(BufGV)); + + const auto *ElemIt = LayoutStruct->element_begin(); + for (Decl *D : BufDecl->decls()) { + if (isa<CXXRecordDecl, EmptyDecl>(D)) // Nothing to do for this declaration. - } else if (isa<FunctionDecl>(it)) { - // A function within an cbuffer is effectively a top-level function, - // as it only refers to globally scoped declarations. - CGM.EmitTopLevelDecl(it); + continue; + if (isa<FunctionDecl>(D)) { + // A function within an cbuffer is effectively a top-level function. + CGM.EmitTopLevelDecl(D); + continue; } + VarDecl *VD = dyn_cast<VarDecl>(D); + if (!VD) + continue; + + QualType VDTy = VD->getType(); + if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { + if (VD->getStorageClass() == SC_Static || + VDTy.getAddressSpace() == LangAS::hlsl_groupshared || + isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { + // Emit static and groupshared variables and resource classes inside + // cbuffer as regular globals + CGM.EmitGlobal(VD); + } else { + // Anything else that is not in the hlsl_constant address space must be + // an empty struct or a zero-sized array and can be ignored + assert(BufDecl->getASTContext().getTypeSize(VDTy) == 0 && + "constant buffer decl with non-zero sized type outside of " + "hlsl_constant address space"); + } + continue; + } + + assert(ElemIt != LayoutStruct->element_end() && + "number of elements in layout struct does not match"); + llvm::Type *LayoutType = *ElemIt++; + + // FIXME: handle resources inside user defined structs + // (llvm/wg-hlsl#175) + + // create global variable for the constant and to metadata list + GlobalVariable *ElemGV = + cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType)); + BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); } + assert(ElemIt == LayoutStruct->element_end() && + "number of elements in layout struct does not match"); + + // add buffer metadata to the module + CGM.getModule() + .getOrInsertNamedMetadata("hlsl.cbs") + ->addOperand(MDNode::get(Ctx, BufGlobals)); } -void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) { - Buffers.emplace_back(Buffer(D)); - addBufferDecls(D, Buffers.back()); +// Creates resource handle type for the HLSL buffer declaration +static const clang::HLSLAttributedResourceType * +createBufferHandleType(const HLSLBufferDecl *BufDecl) { + ASTContext &AST = BufDecl->getASTContext(); + QualType QT = AST.getHLSLAttributedResourceType( + AST.HLSLResourceTy, + QualType(BufDecl->getLayoutStruct()->getTypeForDecl(), 0), + HLSLAttributedResourceType::Attributes(ResourceClass::CBuffer)); + return cast<HLSLAttributedResourceType>(QT.getTypePtr()); +} + +static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl, + SmallVector<unsigned> &Layout) { + assert(Layout.empty() && "expected empty vector for layout"); + assert(BufDecl->hasValidPackoffset()); + + for (Decl *D : BufDecl->decls()) { + if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) { + continue; + } + VarDecl *VD = dyn_cast<VarDecl>(D); + if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) + continue; + assert(VD->hasAttr<HLSLPackOffsetAttr>() && + "expected packoffset attribute on every declaration"); + size_t Offset = VD->getAttr<HLSLPackOffsetAttr>()->getOffsetInBytes(); + Layout.push_back(Offset); + } +} + +// Codegen for HLSLBufferDecl +void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { + + assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet"); + + // create resource handle type for the buffer + const clang::HLSLAttributedResourceType *ResHandleTy = + createBufferHandleType(BufDecl); + + // empty constant buffer is ignored + if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty()) + return; + + // create global variable for the constant buffer + SmallVector<unsigned> Layout; + if (BufDecl->hasValidPackoffset()) + fillPackoffsetLayout(BufDecl, Layout); + + llvm::TargetExtType *TargetTy = + cast<llvm::TargetExtType>(convertHLSLSpecificType( + ResHandleTy, BufDecl->hasValidPackoffset() ? &Layout : nullptr)); + llvm::GlobalVariable *BufGV = + new GlobalVariable(TargetTy, /*isConstant*/ true, + GlobalValue::LinkageTypes::ExternalLinkage, nullptr, + llvm::formatv("{0}{1}", BufDecl->getName(), + BufDecl->isCBuffer() ? ".cb" : ".tb"), + GlobalValue::NotThreadLocal); + CGM.getModule().insertGlobalVariable(BufGV); + + // Add globals for constant buffer elements and create metadata nodes + emitBufferGlobalsAndMetadata(BufDecl, BufGV); + + // Resource initialization + const HLSLResourceBindingAttr *RBA = + BufDecl->getAttr<HLSLResourceBindingAttr>(); + // FIXME: handle implicit binding if no binding attribute is found + // (llvm/llvm-project#110722) + if (RBA) + createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(), + RBA->getSpaceNumber()); +} + +llvm::TargetExtType * +CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { + const auto Entry = LayoutTypes.find(StructType); + if (Entry != LayoutTypes.end()) + return Entry->getSecond(); + return nullptr; +} + +void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType, + llvm::TargetExtType *LayoutTy) { + assert(getHLSLBufferLayoutType(StructType) == nullptr && + "layout type for this struct already exist"); + LayoutTypes[StructType] = LayoutTy; } void CGHLSLRuntime::finishCodeGen() { @@ -169,28 +263,8 @@ void CGHLSLRuntime::finishCodeGen() { addDxilValVersion(TargetOpts.DxilValidatorVersion, M); generateGlobalCtorDtorCalls(); - - const DataLayout &DL = M.getDataLayout(); - - for (auto &Buf : Buffers) { - layoutBuffer(Buf, DL); - GlobalVariable *GV = replaceBuffer(Buf); - M.insertGlobalVariable(GV); - llvm::hlsl::ResourceClass RC = Buf.IsCBuffer - ? llvm::hlsl::ResourceClass::CBuffer - : llvm::hlsl::ResourceClass::SRV; - llvm::hlsl::ResourceKind RK = Buf.IsCBuffer - ? llvm::hlsl::ResourceKind::CBuffer - : llvm::hlsl::ResourceKind::TBuffer; - addBufferResourceAnnotation(GV, RC, RK, /*IsROV=*/false, - llvm::hlsl::ElementType::Invalid, Buf.Binding); - } } -CGHLSLRuntime::Buffer::Buffer(const HLSLBufferDecl *D) - : Name(D->getName()), IsCBuffer(D->isCBuffer()), - Binding(D->getAttr<HLSLResourceBindingAttr>()) {} - void CGHLSLRuntime::addBufferResourceAnnotation(llvm::GlobalVariable *GV, llvm::hlsl::ResourceClass RC, llvm::hlsl::ResourceKind RK, @@ -524,21 +598,15 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() { } } -// Returns true if the type is an HLSL resource class -static bool isResourceRecordType(const clang::Type *Ty) { - return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; -} - -static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, - llvm::GlobalVariable *GV, unsigned Slot, - unsigned Space) { +static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV, + unsigned Slot, unsigned Space) { LLVMContext &Ctx = CGM.getLLVMContext(); llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx); llvm::Function *InitResFunc = llvm::Function::Create( llvm::FunctionType::get(CGM.VoidTy, false), llvm::GlobalValue::InternalLinkage, - ("_init_resource_" + VD->getName()).str(), CGM.getModule()); + ("_init_resource_" + GV->getName()).str(), CGM.getModule()); InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline); llvm::BasicBlock *EntryBB = @@ -547,20 +615,15 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, const DataLayout &DL = CGM.getModule().getDataLayout(); Builder.SetInsertPoint(EntryBB); - const HLSLAttributedResourceType *AttrResType = - HLSLAttributedResourceType::findHandleTypeOnResource( - VD->getType().getTypePtr()); - - // FIXME: Only simple declarations of resources are supported for now. - // Arrays of resources or resources in user defined classes are - // not implemented yet. - assert(AttrResType != nullptr && - "Resource class must have a handle of HLSLAttributedResourceType"); - - llvm::Type *TargetTy = - CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType); - assert(TargetTy != nullptr && - "Failed to convert resource handle to target type"); + // Make sure the global variable is resource handle (cbuffer) or + // resource class (=class where the first element is a resource handle). + llvm::Type *HandleTy = GV->getValueType(); + assert((HandleTy->isTargetExtTy() || + (HandleTy->isStructTy() && + HandleTy->getStructElementType(0)->isTargetExtTy())) && + "unexpected type of the global"); + if (!HandleTy->isTargetExtTy()) + HandleTy = HandleTy->getStructElementType(0); llvm::Value *Args[] = { llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */ @@ -572,9 +635,9 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */ }; llvm::Value *CreateHandle = Builder.CreateIntrinsic( - /*ReturnType=*/TargetTy, + /*ReturnType=*/HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr, - Twine(VD->getName()).concat("_h")); + Twine(GV->getName()).concat("_h")); llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0); Builder.CreateAlignedStore(CreateHandle, HandleRef, @@ -601,8 +664,7 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, // not implemented yet. return; - createResourceInitFn(CGM, VD, GV, RBA->getSlotNumber(), - RBA->getSpaceNumber()); + createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber()); } llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 8767a2ddceb96..a9da42324a038 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H #define LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H +#include "llvm/ADT/DenseMap.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicsDirectX.h" @@ -46,20 +47,26 @@ } \ } +using ResourceClass = llvm::dxil::ResourceClass; + namespace llvm { class GlobalVariable; class Function; class StructType; +class Metadata; } // namespace llvm namespace clang { +class NamedDecl; class VarDecl; class ParmVarDecl; class InitListExpr; class HLSLBufferDecl; class HLSLResourceBindingAttr; class Type; +class RecordType; class DeclContext; +class HLSLPackOffsetAttr; class FunctionDecl; @@ -126,16 +133,6 @@ class CGHLSLRuntime { unsigned Space; BufferResBinding(HLSLResourceBindingAttr *Attr); }; - struct Buffer { - Buffer(const HLSLBufferDecl *D); - llvm::StringRef Name; - // IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). - bool IsCBuffer; - BufferResBinding Binding; - // Global variable and offset for each constant. - std::vector<std::pair<llvm::GlobalVariable *, unsigned>> Constants; - llvm::StructType *LayoutStruct = nullptr; - }; protected: CodeGenModule &CGM; @@ -147,7 +144,9 @@ class CGHLSLRuntime { CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} virtual ~CGHLSLRuntime() {} - llvm::Type *convertHLSLSpecificType(const Type *T); + llvm::Type * + convertHLSLSpecificType(const Type *T, + SmallVector<unsigned> *Packoffsets = nullptr); void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV); void generateGlobalCtorDtorCalls(); @@ -163,6 +162,10 @@ class CGHLSLRuntime { llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB); + llvm::TargetExtType * + getHLSLBufferLayoutType(const RecordType *LayoutStructTy); + void addHLSLBufferLayoutType(const RecordType *LayoutStructTy, + llvm::TargetExtType *LayoutTy); void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E); private: @@ -171,10 +174,11 @@ class CGHLSLRuntime { llvm::hlsl::ResourceKind RK, bool IsROV, llvm::hlsl::ElementType ET, BufferResBinding &Binding); - void addConstant(VarDecl *D, Buffer &CB); - void addBufferDecls(const DeclContext *DC, Buffer &CB); + void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, + llvm::GlobalVariable *BufGV); llvm::Triple::ArchType getArch(); - llvm::SmallVector<Buffer> Buffers; + + llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 868ec847b9634..05ab6671453f8 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -106,6 +106,7 @@ add_clang_library(clangCodeGen ConstantInitBuilder.cpp CoverageMappingGen.cpp ItaniumCXXABI.cpp + HLSLBufferLayoutBuilder.cpp LinkInModulesPass.cpp MacroPPCallbacks.cpp MicrosoftCXXABI.cpp diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp new file mode 100644 index 0000000000000..1ae00023ab2bc --- /dev/null +++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp @@ -0,0 +1,229 @@ +//===- HLSLBufferLayoutBuilder.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "HLSLBufferLayoutBuilder.h" +#include "CGHLSLRuntime.h" +#include "CodeGenModule.h" +#include "clang/AST/Type.h" + +//===----------------------------------------------------------------------===// +// Implementation of constant buffer layout common between DirectX and +// SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +using namespace clang; +using namespace clang::CodeGen; + +namespace { + +// Creates a new array type with the same dimentions but with the new +// element type. +static llvm::Type * +createArrayWithNewElementType(CodeGenModule &CGM, + const ConstantArrayType *ArrayType, + llvm::Type *NewElemType) { + const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual(); + if (ArrayElemType->isConstantArrayType()) + NewElemType = createArrayWithNewElementType( + CGM, cast<const ConstantArrayType>(ArrayElemType), NewElemType); + return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize()); +} + +// Returns the size of a scalar or vector in bytes +static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) { + assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); + if (Ty->isVectorTy()) { + llvm::FixedVectorType *FVT = cast<llvm::FixedVectorType>(Ty); + return FVT->getNumElements() * + (FVT->getElementType()->getScalarSizeInBits() / 8); + } + return Ty->getScalarSizeInBits() / 8; +} + +} // namespace + +namespace clang { +namespace CodeGen { + +// Creates a layout type for given struct with HLSL constant buffer layout +// taking into account Packoffsets, if provided. +// Previously created layout types are cached by CGHLSLRuntime. +// +// The function iterates over all fields of the StructType (including base +// classes) and calls layoutField to converts each field to its corresponding +// LLVM type and to calculate its HLSL constant buffer layout. Any embedded +// structs (or arrays of structs) are converted to target layout types as well. +llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType( + const RecordType *StructType, + const llvm::SmallVector<unsigned> *Packoffsets) { + + // check if we already have the layout type for this struct + if (llvm::TargetExtType *Ty = + CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType)) + return Ty; + + SmallVector<unsigned> Layout; + SmallVector<llvm::Type *> LayoutElements; + unsigned Index = 0; // packoffset index + unsigned EndOffset = 0; + + // reserve first spot in the layout vector for buffer size + Layout.push_back(0); + + // iterate over all fields of the record, including fields on base classes + llvm::SmallVector<const RecordType *> RecordTypes; + RecordTypes.push_back(StructType); + while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { + CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); + assert(D->getNumBases() == 1 && + "HLSL doesn't support multiple inheritance"); + RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>()); + } + while (!RecordTypes.empty()) { + const RecordType *RT = RecordTypes.back(); + RecordTypes.pop_back(); + + for (const auto *FD : RT->getDecl()->fields()) { + assert(!Packoffsets || Index < Packoffsets->size() && + "number of elements in layout struct does not " + "match number of packoffset annotations"); + + if (!layoutField(FD, EndOffset, Layout, LayoutElements, + Packoffsets ? (*Packoffsets)[Index] : -1)) + return nullptr; + Index++; + } + } + + // set the size of the buffer + Layout[0] = EndOffset; + + // create the layout struct type; anonymous struct have empty name but + // non-empty qualified name + const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl(); + std::string Name = + Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString(); + llvm::StructType *StructTy = + llvm::StructType::create(LayoutElements, Name, true); + + // create target layout type + llvm::TargetExtType *NewLayoutTy = llvm::TargetExtType::get( + CGM.getLLVMContext(), LayoutTypeName, {StructTy}, Layout); + if (NewLayoutTy) + CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy); + return NewLayoutTy; +} + +// The function converts a single field of HLSL Buffer to its corresponding +// LLVM type and calculates it's layout. Any embedded structs (or +// arrays of structs) are converted to target layout types as well. +// The converted type is appended to the LayoutElements list, the element +// offset is added to the Layout list and the EndOffset updated to the offset +// just after the lay-ed out element (which is basically the size of the +// buffer). +// Returns true if the conversion was successful. +// The packoffset parameter contains the field's layout offset provided by the +// user or -1 if there was no packoffset (or register(cX)) annotation. +bool HLSLBufferLayoutBuilder::layoutField( + const FieldDecl *FD, unsigned &EndOffset, SmallVector<unsigned> &Layout, + SmallVector<llvm::Type *> &LayoutElements, int Packoffset) { + + // Size of element; for arrays this is a size of a single element in the + // array. Total array size of calculated as (ArrayCount-1) * ArrayStride + + // ElemSize. + unsigned ElemSize = 0; + unsigned ElemOffset = 0; + unsigned ArrayCount = 1; + unsigned ArrayStride = 0; + + const unsigned BufferRowAlign = 16U; + unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign); + + llvm::Type *ElemLayoutTy = nullptr; + QualType FieldTy = FD->getType(); + + if (FieldTy->isConstantArrayType()) { + // Unwrap array to find the element type and get combined array size. + QualType Ty = FieldTy; + while (Ty->isConstantArrayType()) { + const ConstantArrayType *ArrayTy = cast<ConstantArrayType>(Ty); + ArrayCount *= ArrayTy->getSExtSize(); + Ty = ArrayTy->getElementType(); + } + // For array of structures, create a new array with a layout type + // instead of the structure type. + if (Ty->isStructureType()) { + llvm::Type *NewTy = + cast<llvm::TargetExtType>(createLayoutType(Ty->getAsStructureType())); + if (!NewTy) + return false; + assert(isa<llvm::TargetExtType>(NewTy) && "expected target type"); + ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0); + ElemLayoutTy = createArrayWithNewElementType( + CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy); + } else { + // Array of vectors or scalars + ElemSize = + getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty)); + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + } + ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign); + ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset; + + } else if (FieldTy->isStructureType()) { + // Create a layout type for the structure + ElemLayoutTy = createLayoutType(FieldTy->getAsStructureType()); + if (!ElemLayoutTy) + return false; + assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type"); + ElemSize = cast<llvm::TargetExtType>(ElemLayoutTy)->getIntParameter(0); + ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset; + + } else { + // scalar or vector - find element size and alignment + unsigned Align = 0; + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + if (ElemLayoutTy->isVectorTy()) { + // align vectors by sub element size + const llvm::FixedVectorType *FVT = + cast<llvm::FixedVectorType>(ElemLayoutTy); + unsigned SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8; + ElemSize = FVT->getNumElements() * SubElemSize; + Align = SubElemSize; + } else { + assert(ElemLayoutTy->isIntegerTy() || ElemLayoutTy->isFloatingPointTy()); + ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8; + Align = ElemSize; + } + + // calculate or get element offset for the vector or scalar + if (Packoffset != -1) { + ElemOffset = Packoffset; + } else { + ElemOffset = llvm::alignTo(EndOffset, Align); + // if the element does not fit, move it to the next row + if (ElemOffset + ElemSize > NextRowOffset) + ElemOffset = NextRowOffset; + } + } + + // Update end offset of the layout; do not update it if the EndOffset + // is already bigger than the new value (which may happen with unordered + // packoffset annotations) + unsigned NewEndOffset = + ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; + EndOffset = std::max<unsigned>(EndOffset, NewEndOffset); + + // add the layout element and offset to the lists + Layout.push_back(ElemOffset); + LayoutElements.push_back(ElemLayoutTy); + return true; +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h new file mode 100644 index 0000000000000..57bb17c557b9c --- /dev/null +++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h @@ -0,0 +1,48 @@ +//===- HLSLBufferLayoutBuilder.h ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/DerivedTypes.h" + +namespace clang { +class RecordType; +class FieldDecl; + +namespace CodeGen { +class CodeGenModule; + +//===----------------------------------------------------------------------===// +// Implementation of constant buffer layout common between DirectX and +// SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +class HLSLBufferLayoutBuilder { +private: + CodeGenModule &CGM; + llvm::StringRef LayoutTypeName; + +public: + HLSLBufferLayoutBuilder(CodeGenModule &CGM, llvm::StringRef LayoutTypeName) + : CGM(CGM), LayoutTypeName(LayoutTypeName) {} + + // Returns LLVM target extension type with the name LayoutTypeName + // for given structure type and layout data. The first number in + // the Layout is the size followed by offsets for each struct element. + llvm::TargetExtType * + createLayoutType(const RecordType *StructType, + const llvm::SmallVector<unsigned> *Packoffsets = nullptr); + +private: + bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset, + llvm::SmallVector<unsigned> &Layout, + llvm::SmallVector<llvm::Type *> &LayoutElements, + int Packoffset); +}; + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h index 4a66683a3b91f..86057c14a549e 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -439,7 +439,9 @@ class TargetCodeGenInfo { } /// Return an LLVM type that corresponds to a HLSL type - virtual llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const { + virtual llvm::Type * + getHLSLType(CodeGenModule &CGM, const Type *T, + const SmallVector<unsigned> *Packoffsets = nullptr) const { return nullptr; } diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index 7935f7ae37004..77091eb45f5cf 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -7,8 +7,13 @@ //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" +#include "CodeGenModule.h" +#include "HLSLBufferLayoutBuilder.h" #include "TargetInfo.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Type.h" using namespace clang; using namespace clang::CodeGen; @@ -24,11 +29,14 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo { DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) : TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(CGT)) {} - llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const override; + llvm::Type *getHLSLType( + CodeGenModule &CGM, const Type *T, + const SmallVector<unsigned> *Packoffsets = nullptr) const override; }; -llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, - const Type *Ty) const { +llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector<unsigned> *Packoffsets) const { auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty); if (!ResType) return nullptr; @@ -56,9 +64,19 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, return llvm::TargetExtType::get(Ctx, TypeName, {ElemType}, Ints); } - case llvm::dxil::ResourceClass::CBuffer: - llvm_unreachable("dx.CBuffer handles are not implemented yet"); - break; + case llvm::dxil::ResourceClass::CBuffer: { + QualType ContainedTy = ResType->getContainedType(); + if (ContainedTy.isNull() || !ContainedTy->isStructureType()) + return nullptr; + + llvm::Type *BufferLayoutTy = + HLSLBufferLayoutBuilder(CGM, "dx.Layout") + .createLayoutType(ContainedTy->getAsStructureType(), Packoffsets); + if (!BufferLayoutTy) + return nullptr; + + return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {BufferLayoutTy}); + } case llvm::dxil::ResourceClass::Sampler: llvm_unreachable("dx.Sampler handles are not implemented yet"); break; diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index b81ed29a5159b..c94db31ae1a89 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -52,7 +52,9 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { unsigned getOpenCLKernelCallingConv() const override; llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override; - llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override; + llvm::Type *getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector<unsigned> *Packoffsets = nullptr) const override; llvm::Type *getSPIRVImageTypeFromHLSLResource( const HLSLAttributedResourceType::Attributes &attributes, llvm::Type *ElementType, llvm::LLVMContext &Ctx) const; @@ -367,8 +369,9 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM, return nullptr; } -llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, - const Type *Ty) const { +llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector<unsigned> *Packoffsets) const { auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty); if (!ResType) return nullptr; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 362df485a025c..d95763b22a819 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14295,6 +14295,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { if (getLangOpts().OpenCL && Var->getType().getAddressSpace() == LangAS::opencl_local) return; + + // In HLSL, objects in the hlsl_constant address space are initialized + // externally, so don't synthesize an implicit initializer. + if (getLangOpts().HLSL && + Var->getType().getAddressSpace() == LangAS::hlsl_constant) + return; + // C++03 [dcl.init]p9: // If no initializer is specified for an object, and the // object is of (possibly cv-qualified) non-POD class type (or diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 20275ded8a561..502a1429ff6e3 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -239,6 +239,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { // Make sure there is no overlap in packoffset - sort PackOffsetVec by offset // and compare adjacent values. + bool IsValid = true; ASTContext &Context = S.getASTContext(); std::sort(PackOffsetVec.begin(), PackOffsetVec.end(), [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS, @@ -257,8 +258,10 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { VarDecl *NextVar = PackOffsetVec[i + 1].first; S.Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap) << NextVar << Var; + IsValid = false; } } + BufDecl->setHasValidPackoffset(IsValid); } // Returns true if the array has a zero size = if any of the dimensions is 0 @@ -500,7 +503,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { } } LS->completeDefinition(); - BufDecl->addDecl(LS); + BufDecl->addLayoutStruct(LS); } // Handle end of cbuffer/tbuffer declaration diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl deleted file mode 100644 index 825e7b8161a60..0000000000000 --- a/clang/test/CodeGenHLSL/cbuf.hlsl +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// CHECK: @a = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @b = external addrspace(2) externally_initialized global double, align 8 -// CHECK: @c = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @d = external addrspace(2) externally_initialized global double, align 8 - -// CHECK: @[[CB:.+]] = external constant { float, double } -cbuffer A : register(b0, space2) { - float a; - double b; -} - -// CHECK: @[[TB:.+]] = external constant { float, double } -tbuffer A : register(t2, space1) { - float c; - double d; -} - -float foo() { -// CHECK: load float, ptr addrspace(2) @a, align 4 -// CHECK: load double, ptr addrspace(2) @b, align 8 -// CHECK: load float, ptr addrspace(2) @c, align 4 -// CHECK: load double, ptr addrspace(2) @d, align 8 - return a + b + c*d; -} - -// CHECK: !hlsl.cbufs = !{![[CBMD:[0-9]+]]} -// CHECK: ![[CBMD]] = !{ptr @[[CB]], i32 13, i32 0, i1 false, i32 0, i32 2} diff --git a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl b/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl deleted file mode 100644 index 13c401d428331..0000000000000 --- a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// Make sure cbuffer inside namespace works. - -// CHECK: @_ZN2n02n11aE = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @_ZN2n01bE = external addrspace(2) externally_initialized global float, align 4 - -// CHECK: @[[CB:.+]] = external constant { float } -// CHECK: @[[TB:.+]] = external constant { float } -namespace n0 { -namespace n1 { - cbuffer A { - float a; - } -} - tbuffer B { - float b; - } -} - -float foo() { -// CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 -// CHECK: load float, ptr addrspace(2) @_ZN2n01bE, align 4 - return n0::n1::a + n0::b; -} diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl new file mode 100644 index 0000000000000..38093c6dfacd7 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -0,0 +1,197 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \ +// RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// CHECK: %__cblayout_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }> +// CHECK: %__cblayout_CBVectors = type <{ <3 x float>, <3 x double>, <2 x half>, <3 x i64>, <4 x i32>, <3 x i16>, <3 x i64> }> +// CHECK: %__cblayout_CBArrays = type <{ [3 x float], [2 x <3 x double>], [2 x [2 x half]], [3 x i64], [2 x [3 x [4 x <4 x i32>]]], [1 x i16], [2 x i64], [4 x i32] }> +// CHECK: %__cblayout_CBStructs = type <{ target("dx.Layout", %A, 8, 0), target("dx.Layout", %B, 14, 0, 8), +// CHECK-SAME: target("dx.Layout", %C, 24, 0, 16), [5 x target("dx.Layout", %A, 8, 0)], +// CHECK-SAME: target("dx.Layout", %__cblayout_D, 94, 0), half, <3 x i16> }> + +// CHECK: %A = type <{ <2 x float> }> +// CHECK: %B = type <{ <2 x float>, <3 x i16> }> +// CHECK: %C = type <{ i32, target("dx.Layout", %A, 8, 0) }> +// CHECK: %__cblayout_D = type <{ [2 x [3 x target("dx.Layout", %B, 14, 0, 8)]] }> + +// CHECK: %__cblayout_CBMix = type <{ [2 x target("dx.Layout", %Test, 8, 0, 4)], float, [3 x [2 x <2 x float>]], float, +// CHECK-SAME: target("dx.Layout", %anon, 4, 0), double, target("dx.Layout", %anon.0, 8, 0), float, <1 x double>, i16 }> + +// CHECK: %Test = type <{ float, float }> +// CHECK: %anon = type <{ float }> +// CHECK: %anon.0 = type <{ <2 x i32> }> + +cbuffer CBScalars : register(b1, space5) { + float a1; + double a2; + float16_t a3; + uint64_t a4; + int a5; + uint16_t a6; + bool a7; + int64_t a8; +} + +// CHECK: @CBScalars.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, +// CHECK-SAME: 56, 0, 8, 16, 24, 32, 36, 40, 48)) +// CHECK: @a1 = external addrspace(2) global float, align 4 +// CHECK: @a2 = external addrspace(2) global double, align 8 +// CHECK: @a3 = external addrspace(2) global half, align 2 +// CHECK: @a4 = external addrspace(2) global i64, align 8 +// CHECK: @a5 = external addrspace(2) global i32, align 4 +// CHECK: @a6 = external addrspace(2) global i16, align 2 +// CHECK: @a7 = external addrspace(2) global i32, align 4 +// CHECK: @a8 = external addrspace(2) global i64, align 8 + +cbuffer CBVectors { + float3 b1; + double3 b2; + float16_t2 b3; + uint64_t3 b4; + int4 b5; + uint16_t3 b6; + int64_t3 b7; + // FIXME: add a bool vectors after llvm-project/llvm#91639 is added +} + +// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBVectors, +// CHECK-SAME: 136, 0, 16, 40, 48, 80, 96, 112)) +// CHECK: @b1 = external addrspace(2) global <3 x float>, align 16 +// CHECK: @b2 = external addrspace(2) global <3 x double>, align 32 +// CHECK: @b3 = external addrspace(2) global <2 x half>, align 4 +// CHECK: @b4 = external addrspace(2) global <3 x i64>, align 32 +// CHECK: @b5 = external addrspace(2) global <4 x i32>, align 16 +// CHECK: @b6 = external addrspace(2) global <3 x i16>, align 8 +// CHECK: @b7 = external addrspace(2) global <3 x i64>, align 32 + +cbuffer CBArrays : register(b2) { + float c1[3]; + double3 c2[2]; + float16_t c3[2][2]; + uint64_t c4[3]; + int4 c5[2][3][4]; + uint16_t c6[1]; + int64_t c7[2]; + bool c8[4]; +} + +// CHECK: @CBArrays.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, +// CHECK-SAME: 708, 0, 48, 112, 176, 224, 608, 624, 656)) +// CHECK: @c1 = external addrspace(2) global [3 x float], align 4 +// CHECK: @c2 = external addrspace(2) global [2 x <3 x double>], align 32 +// CHECK: @c3 = external addrspace(2) global [2 x [2 x half]], align 2 +// CHECK: @c4 = external addrspace(2) global [3 x i64], align 8 +// CHECK: @c5 = external addrspace(2) global [2 x [3 x [4 x <4 x i32>]]], align 16 +// CHECK: @c6 = external addrspace(2) global [1 x i16], align 2 +// CHECK: @c7 = external addrspace(2) global [2 x i64], align 8 +// CHECK: @c8 = external addrspace(2) global [4 x i32], align 4 + +struct Empty {}; + +struct A { + float2 f1; +}; + +struct B : A { + uint16_t3 f2; +}; + +struct C { + int i; + A f3; +}; + +struct D { + B array_of_B[2][3]; + Empty es; +}; + +// CHECK: @CBStructs.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBStructs, +// CHECK-SAME: 246, 0, 16, 32, 64, 144, 238, 240)) +// CHECK: @a = external addrspace(2) global target("dx.Layout", %A, 8, 0), align 8 +// CHECK: @b = external addrspace(2) global target("dx.Layout", %B, 14, 0, 8), align 8 +// CHECK: @c = external addrspace(2) global target("dx.Layout", %C, 24, 0, 16), align 8 +// CHECK: @array_of_A = external addrspace(2) global [5 x target("dx.Layout", %A, 8, 0)], align 8 +// CHECK: @d = external addrspace(2) global target("dx.Layout", %__cblayout_D, 94, 0), align 8 +// CHECK: @e = external addrspace(2) global half, align 2 +// CHECK: @f = external addrspace(2) global <3 x i16>, align 8 + +cbuffer CBStructs { + A a; + B b; + C c; + A array_of_A[5]; + D d; + half e; + uint16_t3 f; +}; + +struct Test { + float a, b; +}; + +// CHECK: @CBMix.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBMix, +// CHECK-SAME: 170, 0, 24, 32, 120, 128, 136, 144, 152, 160, 168)) +// CHECK: @test = external addrspace(2) global [2 x target("dx.Layout", %Test, 8, 0, 4)], align 4 +// CHECK: @f1 = external addrspace(2) global float, align 4 +// CHECK: @f2 = external addrspace(2) global [3 x [2 x <2 x float>]], align 8 +// CHECK: @f3 = external addrspace(2) global float, align 4 +// CHECK: @f4 = external addrspace(2) global target("dx.Layout", %anon, 4, 0), align 4 +// CHECK: @f5 = external addrspace(2) global double, align 8 +// CHECK: @f6 = external addrspace(2) global target("dx.Layout", %anon.0, 8, 0), align 8 +// CHECK: @f7 = external addrspace(2) global float, align 4 +// CHECK: @f8 = external addrspace(2) global <1 x double>, align 8 +// CHECK: @f9 = external addrspace(2) global i16, align 2 + +cbuffer CBMix { + Test test[2]; + float f1; + float2 f2[3][2]; + float f3; + struct { float c; } f4; + double f5; + struct { int2 i; } f6; + float f7; + vector<double,1> f8; + uint16_t f9; +}; + +// CHECK: define internal void @_init_resource_CBScalars.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBScalarss_56_0_8_16_24_32_36_40_48tt(i32 5, i32 1, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) %CBScalars.cb_h, ptr @CBScalars.cb, align 4 + +// CHECK: define internal void @_init_resource_CBArrays.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBArrayss_708_0_48_112_176_224_608_624_656tt(i32 0, i32 2, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656)) %CBArrays.cb_h, ptr @CBArrays.cb, align 4 + +RWBuffer<float> Buf; + +[numthreads(4,1,1)] +void main() { + Buf[0] = a1 + b1.z + c1[2] + a.f1.y + f1; +} + +// CHECK: define internal void @_GLOBAL__sub_I_cbuffer.hlsl() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_init_resource_CBScalars.cb() +// CHECK-NEXT: call void @_init_resource_CBArrays.cb() + +// CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]} + +// CHECK: ![[CBSCALARS]] = !{ptr @CBScalars.cb, ptr addrspace(2) @a1, ptr addrspace(2) @a2, ptr addrspace(2) @a3, ptr addrspace(2) @a4, +// CHECK-SAME: ptr addrspace(2) @a5, ptr addrspace(2) @a6, ptr addrspace(2) @a7, ptr addrspace(2) @a8} + +// CHECK: ![[CBVECTORS]] = !{ptr @CBVectors.cb, ptr addrspace(2) @b1, ptr addrspace(2) @b2, ptr addrspace(2) @b3, ptr addrspace(2) @b4, +// CHECK-SAME: ptr addrspace(2) @b5, ptr addrspace(2) @b6, ptr addrspace(2) @b7} + +// CHECK: ![[CBARRAYS]] = !{ptr @CBArrays.cb, ptr addrspace(2) @c1, ptr addrspace(2) @c2, ptr addrspace(2) @c3, ptr addrspace(2) @c4, +// CHECK-SAME: ptr addrspace(2) @c5, ptr addrspace(2) @c6, ptr addrspace(2) @c7, ptr addrspace(2) @c8} + +// CHECK: ![[CBSTRUCTS]] = !{ptr @CBStructs.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c, ptr addrspace(2) @array_of_A, +// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @f} + +// CHECK: ![[CBMIX]] = !{ptr @CBMix.cb, ptr addrspace(2) @test, ptr addrspace(2) @f1, ptr addrspace(2) @f2, ptr addrspace(2) @f3, +// CHECK-SAME: ptr addrspace(2) @f4, ptr addrspace(2) @f5, ptr addrspace(2) @f6, ptr addrspace(2) @f7, ptr addrspace(2) @f8, ptr addrspace(2) @f9} diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl new file mode 100644 index 0000000000000..393ca3825c638 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// Make sure cbuffer inside namespace works. + +// CHECK: %"n0::n1::__cblayout_A" = type <{ float }> +// CHECK: %"n0::__cblayout_B" = type <{ float }> +// CHECK: %"n0::n2::__cblayout_C" = type <{ float, target("dx.Layout", %"n0::Foo", 4, 0) }> +// CHECK: %"n0::Foo" = type <{ float }> + +// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n1::__cblayout_A", 4, 0)) +// CHECK: @_ZN2n02n11aE = external addrspace(2) global float, align 4 + +// CHECK: @B.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::__cblayout_B", 4, 0)) +// CHECK: @_ZN2n01aE = external addrspace(2) global float, align 4 + +// CHECK: @C.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n2::__cblayout_C", 20, 0, 16)) +// CHECK: @_ZN2n02n21aE = external addrspace(2) global float, align 4 +// CHECK: external addrspace(2) global target("dx.Layout", %"n0::Foo", 4, 0), align 4 + +namespace n0 { + struct Foo { + float f; + }; + + namespace n1 { + cbuffer A { + float a; + } + } + cbuffer B { + float a; + } + namespace n2 { + cbuffer C { + float a; + Foo b; + } + } +} + +float foo() { + // CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 + // CHECK: load float, ptr addrspace(2) @_ZN2n01aE, align 4 + // CHECK: load float, ptr addrspace(2) @_ZN2n02n21aE, align 4 + return n0::n1::a + n0::a + n0::n2::a; +} + +[numthreads(4,1,1)] +void main() {} + +// CHECK: !hlsl.cbs = !{![[A:[0-9]+]], ![[B:[0-9]+]], ![[C:[0-9]+]]} +// CHECK: [[A]] = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE} +// CHECK: [[B]] = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE} +// CHECK: [[C]] = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl new file mode 100644 index 0000000000000..870593986a976 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-compute %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }> + +// CHECK: @CB.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88)) +// CHECK: @a = external addrspace(2) global float, align 4 +// CHECK: @b = external addrspace(2) global double, align 8 +// CHECK: @c = external addrspace(2) global <2 x i32>, align 8 + +cbuffer CB : register(b1, space3) { + float a : packoffset(c1.x); + double b : packoffset(c10.z); + int2 c : packoffset(c5.z); +} + +// CHECK: define internal void @_init_resource_CB.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBs_176_16_168_88tt(i32 3, i32 1, i32 1, i32 0, i1 false) + +float foo() { + // CHECK: load float, ptr addrspace(2) @a, align 4 + // CHECK: load double, ptr addrspace(2) @b, align 8 + return a + b; +} +// CHECK: define internal void @_GLOBAL__sub_I_cbuffer_with_packoffset.hlsl() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_init_resource_CB.cb() + +[numthreads(4,1,1)] +void main() { + foo(); +} + +// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} +// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl new file mode 100644 index 0000000000000..99f40d8fc93d7 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// CHECK: %__cblayout_A = type <{ float }> + +// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_A, 4, 0)) +// CHECK: @a = external addrspace(2) global float, align 4 +// CHECK-DAG: @_ZL1b = internal global float 3.000000e+00, align 4 +// CHECK-NOT: @B.cb + +cbuffer A { + float a; + static float b = 3; + float foo() { return a + b; } +} + +cbuffer B { + // intentionally empty +} + +// CHECK: define {{.*}} float @_Z3foov() #0 { +// CHECK: load float, ptr addrspace(2) @a, align 4 + +extern float bar() { + return foo(); +} + +// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} +// CHECK: ![[CB]] = !{ptr @A.cb, ptr addrspace(2) @a} diff --git a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl b/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl deleted file mode 100644 index 25f51cce2017d..0000000000000 --- a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -cbuffer A { - // CHECK: @a = external addrspace(2) externally_initialized global float, align 4 - float a; - // CHECK: @_ZL1b = internal global float 3.000000e+00, align 4 - static float b = 3; - float foo() { return a + b; } -} -// CHECK: @[[CB:.+]] = external constant { float } - -// CHECK:define {{.*}} float @_Z3foov() -// CHECK:load float, ptr addrspace(2) @a, align 4 -// CHECK:load float, ptr @_ZL1b, align 4 - -float bar() { - return foo(); -} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits