diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -128,6 +128,7 @@ case Decl::Concept: case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: + case Decl::HLSLBuffer: // None of these decls require codegen support. return; diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -15,21 +15,48 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H #define LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class GlobalVariable; +class StructType; +} // namespace llvm + namespace clang { +class HLSLBufferDecl; +class VarDecl; +class DeclContext; namespace CodeGen { class CodeGenModule; class CGHLSLRuntime { +public: + struct CBuffer { + CBuffer(HLSLBufferDecl *D); + llvm::StringRef Name; + bool IsCBuffer; + unsigned Reg; + unsigned Space; + // Global variable and offset for each constant. + std::vector> Constants; + llvm::StructType *LayoutStruct = nullptr; + }; + protected: CodeGenModule &CGM; public: CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} virtual ~CGHLSLRuntime() {} - + void addCBuffer(HLSLBufferDecl *D); void finishCodeGen(); + +private: + void addConstant(VarDecl *D, CBuffer &CB); + void addCBufferDecls(DeclContext *DC, CBuffer &CB); + SmallVector CBuffers; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// #include "CGHLSLRuntime.h" +#include "CGDebugInfo.h" #include "CodeGenModule.h" + #include "clang/Basic/TargetOptions.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -23,6 +25,7 @@ using namespace llvm; namespace { + void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { // The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs. // Assume ValVersionStr is legal here. @@ -42,11 +45,138 @@ StringRef DxilValKey = "dx.valver"; M.addModuleFlag(llvm::Module::ModFlagBehavior::AppendUnique, DxilValKey, Val); } + +void layoutCBuffer(CGHLSLRuntime::CBuffer &CB, const DataLayout &DL) { + // FIXME: support legacy cbuffer layout. + std::vector EltTys; + for (auto &Const : CB.Constants) { + auto *GV = Const.first; + Const.second = EltTys.size(); + auto *Ty = GV->getValueType(); + EltTys.emplace_back(Ty); + } + CB.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys); +} + +GlobalVariable *replaceCBuffer(CGHLSLRuntime::CBuffer &CB) { + const unsigned CBufferAddressSpace = 4; + const unsigned TBufferAddressSpace = 5; + // Create global variable for CB. + GlobalVariable *CBGV = new GlobalVariable( + CB.LayoutStruct, /*isConstant*/ true, + GlobalValue::LinkageTypes::ExternalLinkage, nullptr, CB.Name + ".cb.", + GlobalValue::NotThreadLocal, + CB.IsCBuffer ? CBufferAddressSpace : TBufferAddressSpace); + + IRBuilder<> B(CBGV->getContext()); + Value *ZeroIdx = B.getInt32(0); + // Replace Const use with CB use. + for (auto &Const : CB.Constants) { + auto *EltTy = CB.LayoutStruct->getElementType(Const.second); + auto *GV = Const.first; + unsigned Offset = Const.second; + + Value *GEP = + B.CreateGEP(CB.LayoutStruct, CBGV, {ZeroIdx, B.getInt32(Offset)}); + + auto *GVTy = GV->getValueType(); + assert(EltTy == GVTy && "constant type mismatch"); + + // Cast address space. + GEP = B.CreateAddrSpaceCast(GEP, GVTy->getPointerTo()); + // Replace. + GV->replaceAllUsesWith(GEP); + // Erase GV. + GV->removeDeadConstantUsers(); + GV->eraseFromParent(); + } + return CBGV; +} + +void addResourceBinding(GlobalVariable *GV, CGHLSLRuntime::CBuffer &CB) { + // FIXME: add resource binding to GV. +} + } // namespace +void CGHLSLRuntime::addConstant(VarDecl *D, CBuffer &CB) { + if (D->getStorageClass() == SC_Static) { + // For static inside cbuffer, take as global static. + // Don't add to cbuffer. + CGM.EmitGlobal(D); + return; + } + + auto *GV = cast(CGM.GetAddrOfGlobalVar(D)); + // Add debug info for constVal. + if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) + if (CGM.getCodeGenOpts().getDebugInfo() >= + codegenoptions::DebugInfoKind::LimitedDebugInfo) + DI->EmitGlobalVariable(cast(GV), D); + + // FIXME: support packoffset. + uint32_t Offset = 0; + bool HasUserOffset = false; + + unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX; + CB.Constants.emplace_back(std::make_pair(GV, LowerBound)); +} + +void CGHLSLRuntime::addCBufferDecls(DeclContext *DC, CBuffer &CB) { + for (Decl *it : DC->decls()) { + if (VarDecl *ConstDecl = dyn_cast(it)) { + addConstant(ConstDecl, CB); + } else if (isa(*it)) { + // Nothing to do for this declaration. + } else if (isa(it)) { + // Nothing to do for this declaration. + } else if (isa(it)) { + // A function within an cbuffer is effectively a top-level function, + // as it only refers to globally scoped declarations. + CGM.EmitTopLevelDecl(it); + } else if (NamespaceDecl *ND = dyn_cast(it)) { + addCBufferDecls(ND, CB); + } else { + HLSLBufferDecl *Inner = dyn_cast(it); + if (!Inner) { + DiagnosticsEngine &Diags = CGM.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "invalid decl inside cbuffer"); + Diags.Report(it->getLocation(), DiagID); + return; + } + addCBuffer(Inner); + } + } +} + +void CGHLSLRuntime::addCBuffer(HLSLBufferDecl *D) { + CBuffers.emplace_back(CBuffer(D)); + addCBufferDecls(D, CBuffers.back()); +} + void CGHLSLRuntime::finishCodeGen() { auto &TargetOpts = CGM.getTarget().getTargetOpts(); llvm::Module &M = CGM.getModule(); addDxilValVersion(TargetOpts.DxilValidatorVersion, M); + auto &DL = M.getDataLayout(); + for (auto &CB : CBuffers) { + layoutCBuffer(CB, DL); + auto *GV = replaceCBuffer(CB); + M.getGlobalList().push_back(GV); + addResourceBinding(GV, CB); + } +} + +CGHLSLRuntime::CBuffer::CBuffer(HLSLBufferDecl *D) { + Name = D->getName(); + IsCBuffer = D->isCBuffer(); + if (auto *Binding = D->getAttr()) { + Reg = Binding->getID(); + Space = Binding->getSpace(); + } else { + Reg = UINT_MAX; + Space = 0; + } } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6281,6 +6281,10 @@ DI->EmitAndRetainType(getContext().getEnumType(cast(D))); break; + case Decl::HLSLBuffer: + getHLSLRuntime().addCBuffer(cast(D)); + break; + default: // Make sure we handled everything we should, every other kind is a // non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuf.hlsl @@ -0,0 +1,21 @@ +// RUN: %clang_dxc -Tlib_6_7 -fcgl -Fo - %s | FileCheck %s + +// CHECK: @[[CB:.+]] = external addrspace(4) constant { float, float } +cbuffer A : register(b0, space1) { + float a; + float b; +} + +// CHECK: @[[TB:.+]] = external addrspace(5) constant { float, float } +tbuffer A : register(b2, space1) { + float c; + float d; +} + +float foo() { +// CHECK: load float, ptr addrspacecast (ptr addrspace(4) @[[CB]] to ptr), align 4 +// CHECK: load float, ptr addrspacecast (ptr addrspace(4) getelementptr inbounds ({ float, float }, ptr addrspace(4) @[[CB]], i32 0, i32 1) to ptr), align 4 +// CHECK: load float, ptr addrspacecast (ptr addrspace(5) @[[TB]] to ptr), align 4 +// CHECK: load float, ptr addrspacecast (ptr addrspace(5) getelementptr inbounds ({ float, float }, ptr addrspace(5) @[[TB]], i32 0, i32 1) to ptr), align 4 + return a + b + c*d; +}