[clang] [HLSL] Constant buffers codegen (PR #124886)
Justin Bogner via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 18 15:48:17 PST 2025
================
@@ -115,48 +83,176 @@ 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();
+ assert(isa<llvm::TargetExtType>(BufType) &&
+ "expected target type for HLSL buffer resource");
+ llvm::Type *BufLayoutType =
+ cast<llvm::TargetExtType>(BufType)->getTypeParameter(0);
+ assert(isa<llvm::TargetExtType>(BufLayoutType) &&
+ "expected target type for buffer layout struct");
+ 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);
+ }
+ // 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++;
+
+ // 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));
+ }
+ assert(ElemIt == LayoutStruct->element_end() &&
+ "number of elements in layout struct does not match");
+ // set the size of the buffer
+
+ // add buffer metadata to the module
+ CGM.getModule()
+ .getOrInsertNamedMetadata("hlsl.cbs")
+ ->addOperand(MDNode::get(Ctx, BufGlobals));
+}
+
+// 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);
}
}
-void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) {
- Buffers.emplace_back(Buffer(D));
- addBufferDecls(D, Buffers.back());
+// 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
----------------
bogner wrote:
Let's error out in this case for now - just skipping the resource init is undoubtedly going to cause issues down the line.
https://github.com/llvm/llvm-project/pull/124886
More information about the cfe-commits
mailing list