================ @@ -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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits