173417c51Spython3kgae //===----- CGHLSLRuntime.cpp - Interface to HLSL Runtimes -----------------===// 273417c51Spython3kgae // 373417c51Spython3kgae // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 473417c51Spython3kgae // See https://llvm.org/LICENSE.txt for license information. 573417c51Spython3kgae // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 673417c51Spython3kgae // 773417c51Spython3kgae //===----------------------------------------------------------------------===// 873417c51Spython3kgae // 973417c51Spython3kgae // This provides an abstract class for HLSL code generation. Concrete 1073417c51Spython3kgae // subclasses of this implement code generation for specific HLSL 1173417c51Spython3kgae // runtime libraries. 1273417c51Spython3kgae // 1373417c51Spython3kgae //===----------------------------------------------------------------------===// 1473417c51Spython3kgae 1573417c51Spython3kgae #include "CGHLSLRuntime.h" 16ebe9c7f3SXiang Li #include "CGDebugInfo.h" 1773417c51Spython3kgae #include "CodeGenModule.h" 1852956b0fSHelena Kotas #include "TargetInfo.h" 1922c477f9SChris Bieneman #include "clang/AST/Decl.h" 2073417c51Spython3kgae #include "clang/Basic/TargetOptions.h" 217dbfa7b9SHelena Kotas #include "llvm/IR/GlobalVariable.h" 227dbfa7b9SHelena Kotas #include "llvm/IR/LLVMContext.h" 2373417c51Spython3kgae #include "llvm/IR/Metadata.h" 2473417c51Spython3kgae #include "llvm/IR/Module.h" 257dbfa7b9SHelena Kotas #include "llvm/IR/Value.h" 267dbfa7b9SHelena Kotas #include "llvm/Support/Alignment.h" 277dbfa7b9SHelena Kotas 28bad2e6c8SXiang Li #include "llvm/Support/FormatVariadic.h" 2973417c51Spython3kgae 3073417c51Spython3kgae using namespace clang; 3173417c51Spython3kgae using namespace CodeGen; 32a7183a15SXiang Li using namespace clang::hlsl; 3373417c51Spython3kgae using namespace llvm; 3473417c51Spython3kgae 3573417c51Spython3kgae namespace { 36ebe9c7f3SXiang Li 3773417c51Spython3kgae void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { 3873417c51Spython3kgae // The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs. 3973417c51Spython3kgae // Assume ValVersionStr is legal here. 4073417c51Spython3kgae VersionTuple Version; 4173417c51Spython3kgae if (Version.tryParse(ValVersionStr) || Version.getBuild() || 4273417c51Spython3kgae Version.getSubminor() || !Version.getMinor()) { 4373417c51Spython3kgae return; 4473417c51Spython3kgae } 4573417c51Spython3kgae 4673417c51Spython3kgae uint64_t Major = Version.getMajor(); 47ca4af13eSKazu Hirata uint64_t Minor = *Version.getMinor(); 4873417c51Spython3kgae 4973417c51Spython3kgae auto &Ctx = M.getContext(); 5073417c51Spython3kgae IRBuilder<> B(M.getContext()); 5173417c51Spython3kgae MDNode *Val = MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(Major)), 5273417c51Spython3kgae ConstantAsMetadata::get(B.getInt32(Minor))}); 53a0ecb4a2SXiang Li StringRef DXILValKey = "dx.valver"; 54a0ecb4a2SXiang Li auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey); 55a0ecb4a2SXiang Li DXILValMD->addOperand(Val); 5673417c51Spython3kgae } 57f712c013SXiang Li void addDisableOptimizations(llvm::Module &M) { 58f712c013SXiang Li StringRef Key = "dx.disable_optimizations"; 59f712c013SXiang Li M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, Key, 1); 60f712c013SXiang Li } 61ebe9c7f3SXiang Li // cbuffer will be translated into global variable in special address space. 62ebe9c7f3SXiang Li // If translate into C, 63ebe9c7f3SXiang Li // cbuffer A { 64ebe9c7f3SXiang Li // float a; 65ebe9c7f3SXiang Li // float b; 66ebe9c7f3SXiang Li // } 67ebe9c7f3SXiang Li // float foo() { return a + b; } 68ebe9c7f3SXiang Li // 69ebe9c7f3SXiang Li // will be translated into 70ebe9c7f3SXiang Li // 71ebe9c7f3SXiang Li // struct A { 72ebe9c7f3SXiang Li // float a; 73ebe9c7f3SXiang Li // float b; 74ebe9c7f3SXiang Li // } cbuffer_A __attribute__((address_space(4))); 75ebe9c7f3SXiang Li // float foo() { return cbuffer_A.a + cbuffer_A.b; } 76ebe9c7f3SXiang Li // 77ebe9c7f3SXiang Li // layoutBuffer will create the struct A type. 78ebe9c7f3SXiang Li // replaceBuffer will replace use of global variable a and b with cbuffer_A.a 79ebe9c7f3SXiang Li // and cbuffer_A.b. 80ebe9c7f3SXiang Li // 81ebe9c7f3SXiang Li void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) { 82ebe9c7f3SXiang Li if (Buf.Constants.empty()) 83ebe9c7f3SXiang Li return; 84ebe9c7f3SXiang Li 85ebe9c7f3SXiang Li std::vector<llvm::Type *> EltTys; 86ebe9c7f3SXiang Li for (auto &Const : Buf.Constants) { 87ebe9c7f3SXiang Li GlobalVariable *GV = Const.first; 88ebe9c7f3SXiang Li Const.second = EltTys.size(); 89ebe9c7f3SXiang Li llvm::Type *Ty = GV->getValueType(); 90ebe9c7f3SXiang Li EltTys.emplace_back(Ty); 91ebe9c7f3SXiang Li } 92ebe9c7f3SXiang Li Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys); 93ebe9c7f3SXiang Li } 94ebe9c7f3SXiang Li 95ebe9c7f3SXiang Li GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) { 96ebe9c7f3SXiang Li // Create global variable for CB. 9713163dd8SXiang Li GlobalVariable *CBGV = new GlobalVariable( 9813163dd8SXiang Li Buf.LayoutStruct, /*isConstant*/ true, 99ebe9c7f3SXiang Li GlobalValue::LinkageTypes::ExternalLinkage, nullptr, 10013163dd8SXiang Li llvm::formatv("{0}{1}", Buf.Name, Buf.IsCBuffer ? ".cb." : ".tb."), 101ebe9c7f3SXiang Li GlobalValue::NotThreadLocal); 102ebe9c7f3SXiang Li 103ebe9c7f3SXiang Li return CBGV; 104ebe9c7f3SXiang Li } 105f712c013SXiang Li 10673417c51Spython3kgae } // namespace 10773417c51Spython3kgae 10852956b0fSHelena Kotas llvm::Type *CGHLSLRuntime::convertHLSLSpecificType(const Type *T) { 10952956b0fSHelena Kotas assert(T->isHLSLSpecificType() && "Not an HLSL specific type!"); 11052956b0fSHelena Kotas 11152956b0fSHelena Kotas // Check if the target has a specific translation for this type first. 11252956b0fSHelena Kotas if (llvm::Type *TargetTy = CGM.getTargetCodeGenInfo().getHLSLType(CGM, T)) 11352956b0fSHelena Kotas return TargetTy; 11452956b0fSHelena Kotas 11552956b0fSHelena Kotas llvm_unreachable("Generic handling of HLSL types is not supported."); 11652956b0fSHelena Kotas } 11752956b0fSHelena Kotas 1181cb64d75SFarzon Lotfi llvm::Triple::ArchType CGHLSLRuntime::getArch() { 1191cb64d75SFarzon Lotfi return CGM.getTarget().getTriple().getArch(); 1201cb64d75SFarzon Lotfi } 1211cb64d75SFarzon Lotfi 122ebe9c7f3SXiang Li void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) { 123ebe9c7f3SXiang Li if (D->getStorageClass() == SC_Static) { 124ebe9c7f3SXiang Li // For static inside cbuffer, take as global static. 125ebe9c7f3SXiang Li // Don't add to cbuffer. 126ebe9c7f3SXiang Li CGM.EmitGlobal(D); 127ebe9c7f3SXiang Li return; 128ebe9c7f3SXiang Li } 129ebe9c7f3SXiang Li 130ebe9c7f3SXiang Li auto *GV = cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(D)); 131*d92bac8aSHelena Kotas GV->setExternallyInitialized(true); 132ebe9c7f3SXiang Li // Add debug info for constVal. 133ebe9c7f3SXiang Li if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) 134ebe9c7f3SXiang Li if (CGM.getCodeGenOpts().getDebugInfo() >= 135ebe9c7f3SXiang Li codegenoptions::DebugInfoKind::LimitedDebugInfo) 136ebe9c7f3SXiang Li DI->EmitGlobalVariable(cast<GlobalVariable>(GV), D); 137ebe9c7f3SXiang Li 138ebe9c7f3SXiang Li // FIXME: support packoffset. 139ebe9c7f3SXiang Li // See https://github.com/llvm/llvm-project/issues/57914. 140ebe9c7f3SXiang Li uint32_t Offset = 0; 141ebe9c7f3SXiang Li bool HasUserOffset = false; 142ebe9c7f3SXiang Li 143ebe9c7f3SXiang Li unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX; 144ebe9c7f3SXiang Li CB.Constants.emplace_back(std::make_pair(GV, LowerBound)); 145ebe9c7f3SXiang Li } 146ebe9c7f3SXiang Li 147ebe9c7f3SXiang Li void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) { 148ebe9c7f3SXiang Li for (Decl *it : DC->decls()) { 149ebe9c7f3SXiang Li if (auto *ConstDecl = dyn_cast<VarDecl>(it)) { 150ebe9c7f3SXiang Li addConstant(ConstDecl, CB); 151ebe9c7f3SXiang Li } else if (isa<CXXRecordDecl, EmptyDecl>(it)) { 152ebe9c7f3SXiang Li // Nothing to do for this declaration. 153ebe9c7f3SXiang Li } else if (isa<FunctionDecl>(it)) { 154ebe9c7f3SXiang Li // A function within an cbuffer is effectively a top-level function, 155ebe9c7f3SXiang Li // as it only refers to globally scoped declarations. 156ebe9c7f3SXiang Li CGM.EmitTopLevelDecl(it); 157ebe9c7f3SXiang Li } 158ebe9c7f3SXiang Li } 159ebe9c7f3SXiang Li } 160ebe9c7f3SXiang Li 161ebe9c7f3SXiang Li void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) { 162ebe9c7f3SXiang Li Buffers.emplace_back(Buffer(D)); 163ebe9c7f3SXiang Li addBufferDecls(D, Buffers.back()); 164ebe9c7f3SXiang Li } 165ebe9c7f3SXiang Li 16673417c51Spython3kgae void CGHLSLRuntime::finishCodeGen() { 16773417c51Spython3kgae auto &TargetOpts = CGM.getTarget().getTargetOpts(); 16873417c51Spython3kgae llvm::Module &M = CGM.getModule(); 169a0ecb4a2SXiang Li Triple T(M.getTargetTriple()); 170a0ecb4a2SXiang Li if (T.getArch() == Triple::ArchType::dxil) 17173417c51Spython3kgae addDxilValVersion(TargetOpts.DxilValidatorVersion, M); 172d3c54a17SChris Bieneman 173a8a49923SChris Bieneman generateGlobalCtorDtorCalls(); 174f712c013SXiang Li if (CGM.getCodeGenOpts().OptimizationLevel == 0) 175f712c013SXiang Li addDisableOptimizations(M); 176ebe9c7f3SXiang Li 177ebe9c7f3SXiang Li const DataLayout &DL = M.getDataLayout(); 178ebe9c7f3SXiang Li 179ebe9c7f3SXiang Li for (auto &Buf : Buffers) { 180ebe9c7f3SXiang Li layoutBuffer(Buf, DL); 181ebe9c7f3SXiang Li GlobalVariable *GV = replaceBuffer(Buf); 182823186b1SVasileios Porpodas M.insertGlobalVariable(GV); 183a7183a15SXiang Li llvm::hlsl::ResourceClass RC = Buf.IsCBuffer 184a7183a15SXiang Li ? llvm::hlsl::ResourceClass::CBuffer 185a7183a15SXiang Li : llvm::hlsl::ResourceClass::SRV; 18613163dd8SXiang Li llvm::hlsl::ResourceKind RK = Buf.IsCBuffer 18713163dd8SXiang Li ? llvm::hlsl::ResourceKind::CBuffer 18813163dd8SXiang Li : llvm::hlsl::ResourceKind::TBuffer; 1894f54d715SJustin Bogner addBufferResourceAnnotation(GV, RC, RK, /*IsROV=*/false, 1904f54d715SJustin Bogner llvm::hlsl::ElementType::Invalid, Buf.Binding); 191ebe9c7f3SXiang Li } 192ebe9c7f3SXiang Li } 193ebe9c7f3SXiang Li 19413163dd8SXiang Li CGHLSLRuntime::Buffer::Buffer(const HLSLBufferDecl *D) 19513163dd8SXiang Li : Name(D->getName()), IsCBuffer(D->isCBuffer()), 19613163dd8SXiang Li Binding(D->getAttr<HLSLResourceBindingAttr>()) {} 197ebe9c7f3SXiang Li 19813163dd8SXiang Li void CGHLSLRuntime::addBufferResourceAnnotation(llvm::GlobalVariable *GV, 199a7183a15SXiang Li llvm::hlsl::ResourceClass RC, 20013163dd8SXiang Li llvm::hlsl::ResourceKind RK, 2017a13e410SJustin Bogner bool IsROV, 2024f54d715SJustin Bogner llvm::hlsl::ElementType ET, 20313163dd8SXiang Li BufferResBinding &Binding) { 20413163dd8SXiang Li llvm::Module &M = CGM.getModule(); 20513163dd8SXiang Li 20613163dd8SXiang Li NamedMDNode *ResourceMD = nullptr; 20713163dd8SXiang Li switch (RC) { 208a7183a15SXiang Li case llvm::hlsl::ResourceClass::UAV: 20913163dd8SXiang Li ResourceMD = M.getOrInsertNamedMetadata("hlsl.uavs"); 21013163dd8SXiang Li break; 211a7183a15SXiang Li case llvm::hlsl::ResourceClass::SRV: 21213163dd8SXiang Li ResourceMD = M.getOrInsertNamedMetadata("hlsl.srvs"); 21313163dd8SXiang Li break; 214a7183a15SXiang Li case llvm::hlsl::ResourceClass::CBuffer: 21513163dd8SXiang Li ResourceMD = M.getOrInsertNamedMetadata("hlsl.cbufs"); 21613163dd8SXiang Li break; 21713163dd8SXiang Li default: 21813163dd8SXiang Li assert(false && "Unsupported buffer type!"); 21913163dd8SXiang Li return; 220ebe9c7f3SXiang Li } 22113163dd8SXiang Li assert(ResourceMD != nullptr && 22213163dd8SXiang Li "ResourceMD must have been set by the switch above."); 22313163dd8SXiang Li 22413163dd8SXiang Li llvm::hlsl::FrontendResource Res( 2254f54d715SJustin Bogner GV, RK, ET, IsROV, Binding.Reg.value_or(UINT_MAX), Binding.Space); 22613163dd8SXiang Li ResourceMD->addOperand(Res.getMetadata()); 22713163dd8SXiang Li } 22813163dd8SXiang Li 2294f54d715SJustin Bogner static llvm::hlsl::ElementType 2304f54d715SJustin Bogner calculateElementType(const ASTContext &Context, const clang::Type *ResourceTy) { 2314f54d715SJustin Bogner using llvm::hlsl::ElementType; 2324f54d715SJustin Bogner 2334f54d715SJustin Bogner // TODO: We may need to update this when we add things like ByteAddressBuffer 2344f54d715SJustin Bogner // that don't have a template parameter (or, indeed, an element type). 2354f54d715SJustin Bogner const auto *TST = ResourceTy->getAs<TemplateSpecializationType>(); 2364f54d715SJustin Bogner assert(TST && "Resource types must be template specializations"); 2374f54d715SJustin Bogner ArrayRef<TemplateArgument> Args = TST->template_arguments(); 2384f54d715SJustin Bogner assert(!Args.empty() && "Resource has no element type"); 2394f54d715SJustin Bogner 2404f54d715SJustin Bogner // At this point we have a resource with an element type, so we can assume 2414f54d715SJustin Bogner // that it's valid or we would have diagnosed the error earlier. 2424f54d715SJustin Bogner QualType ElTy = Args[0].getAsType(); 2434f54d715SJustin Bogner 2444f54d715SJustin Bogner // We should either have a basic type or a vector of a basic type. 2454f54d715SJustin Bogner if (const auto *VecTy = ElTy->getAs<clang::VectorType>()) 2464f54d715SJustin Bogner ElTy = VecTy->getElementType(); 2474f54d715SJustin Bogner 2484f54d715SJustin Bogner if (ElTy->isSignedIntegerType()) { 2494f54d715SJustin Bogner switch (Context.getTypeSize(ElTy)) { 2504f54d715SJustin Bogner case 16: 2514f54d715SJustin Bogner return ElementType::I16; 2524f54d715SJustin Bogner case 32: 2534f54d715SJustin Bogner return ElementType::I32; 2544f54d715SJustin Bogner case 64: 2554f54d715SJustin Bogner return ElementType::I64; 2564f54d715SJustin Bogner } 2574f54d715SJustin Bogner } else if (ElTy->isUnsignedIntegerType()) { 2584f54d715SJustin Bogner switch (Context.getTypeSize(ElTy)) { 2594f54d715SJustin Bogner case 16: 2604f54d715SJustin Bogner return ElementType::U16; 2614f54d715SJustin Bogner case 32: 2624f54d715SJustin Bogner return ElementType::U32; 2634f54d715SJustin Bogner case 64: 2644f54d715SJustin Bogner return ElementType::U64; 2654f54d715SJustin Bogner } 2664f54d715SJustin Bogner } else if (ElTy->isSpecificBuiltinType(BuiltinType::Half)) 2674f54d715SJustin Bogner return ElementType::F16; 2684f54d715SJustin Bogner else if (ElTy->isSpecificBuiltinType(BuiltinType::Float)) 2694f54d715SJustin Bogner return ElementType::F32; 2704f54d715SJustin Bogner else if (ElTy->isSpecificBuiltinType(BuiltinType::Double)) 2714f54d715SJustin Bogner return ElementType::F64; 2724f54d715SJustin Bogner 2734f54d715SJustin Bogner // TODO: We need to handle unorm/snorm float types here once we support them 2744f54d715SJustin Bogner llvm_unreachable("Invalid element type for resource"); 2754f54d715SJustin Bogner } 2764f54d715SJustin Bogner 2775dbb92d8SChris Bieneman void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) { 2785dbb92d8SChris Bieneman const Type *Ty = D->getType()->getPointeeOrArrayElementType(); 2795dbb92d8SChris Bieneman if (!Ty) 2805dbb92d8SChris Bieneman return; 2815dbb92d8SChris Bieneman const auto *RD = Ty->getAsCXXRecordDecl(); 2825dbb92d8SChris Bieneman if (!RD) 2835dbb92d8SChris Bieneman return; 284ac319a8dSJoshua Batista // the resource related attributes are on the handle member 285ac319a8dSJoshua Batista // inside the record decl 286ac319a8dSJoshua Batista for (auto *FD : RD->fields()) { 287ac319a8dSJoshua Batista const auto *HLSLResAttr = FD->getAttr<HLSLResourceAttr>(); 2888e35c869SHelena Kotas const HLSLAttributedResourceType *AttrResType = 2898e35c869SHelena Kotas dyn_cast<HLSLAttributedResourceType>(FD->getType().getTypePtr()); 2908e35c869SHelena Kotas if (!HLSLResAttr || !AttrResType) 291ac319a8dSJoshua Batista continue; 2925dbb92d8SChris Bieneman 2938e35c869SHelena Kotas llvm::hlsl::ResourceClass RC = AttrResType->getAttrs().ResourceClass; 29474d8f395SHelena Kotas if (RC == llvm::hlsl::ResourceClass::UAV || 29574d8f395SHelena Kotas RC == llvm::hlsl::ResourceClass::SRV) 29674d8f395SHelena Kotas // UAVs and SRVs have already been converted to use LLVM target types, 29774d8f395SHelena Kotas // we can disable generating of these resource annotations. This will 29874d8f395SHelena Kotas // enable progress on structured buffers with user defined types this 29974d8f395SHelena Kotas // resource annotations code does not handle and it crashes. 30074d8f395SHelena Kotas // This whole function is going to be removed as soon as cbuffers are 30174d8f395SHelena Kotas // converted to target types (llvm/llvm-project #114126). 30274d8f395SHelena Kotas return; 30374d8f395SHelena Kotas 3048e35c869SHelena Kotas bool IsROV = AttrResType->getAttrs().IsROV; 3055dc371e2SJoshua Batista llvm::hlsl::ResourceKind RK = HLSLResAttr->getResourceKind(); 3064f54d715SJustin Bogner llvm::hlsl::ElementType ET = calculateElementType(CGM.getContext(), Ty); 3075dbb92d8SChris Bieneman 30813163dd8SXiang Li BufferResBinding Binding(D->getAttr<HLSLResourceBindingAttr>()); 3094f54d715SJustin Bogner addBufferResourceAnnotation(GV, RC, RK, IsROV, ET, Binding); 3105dbb92d8SChris Bieneman } 311ac319a8dSJoshua Batista } 3125dbb92d8SChris Bieneman 31313163dd8SXiang Li CGHLSLRuntime::BufferResBinding::BufferResBinding( 31413163dd8SXiang Li HLSLResourceBindingAttr *Binding) { 31513163dd8SXiang Li if (Binding) { 31613163dd8SXiang Li llvm::APInt RegInt(64, 0); 31713163dd8SXiang Li Binding->getSlot().substr(1).getAsInteger(10, RegInt); 31813163dd8SXiang Li Reg = RegInt.getLimitedValue(); 31913163dd8SXiang Li llvm::APInt SpaceInt(64, 0); 32013163dd8SXiang Li Binding->getSpace().substr(5).getAsInteger(10, SpaceInt); 32113163dd8SXiang Li Space = SpaceInt.getLimitedValue(); 32213163dd8SXiang Li } else { 32313163dd8SXiang Li Space = 0; 32413163dd8SXiang Li } 3255dbb92d8SChris Bieneman } 326906e41f4SXiang Li 32722c477f9SChris Bieneman void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes( 32822c477f9SChris Bieneman const FunctionDecl *FD, llvm::Function *Fn) { 32922c477f9SChris Bieneman const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>(); 33022c477f9SChris Bieneman assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr"); 3319a478d52SChris Bieneman const StringRef ShaderAttrKindStr = "hlsl.shader"; 33222c477f9SChris Bieneman Fn->addFnAttr(ShaderAttrKindStr, 3335d87ba1cSHelena Kotas llvm::Triple::getEnvironmentTypeName(ShaderAttr->getType())); 334bad2e6c8SXiang Li if (HLSLNumThreadsAttr *NumThreadsAttr = FD->getAttr<HLSLNumThreadsAttr>()) { 335bad2e6c8SXiang Li const StringRef NumThreadsKindStr = "hlsl.numthreads"; 336bad2e6c8SXiang Li std::string NumThreadsStr = 337bad2e6c8SXiang Li formatv("{0},{1},{2}", NumThreadsAttr->getX(), NumThreadsAttr->getY(), 338bad2e6c8SXiang Li NumThreadsAttr->getZ()); 339bad2e6c8SXiang Li Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr); 340bad2e6c8SXiang Li } 3410f77bdd2SXiang Li if (HLSLWaveSizeAttr *WaveSizeAttr = FD->getAttr<HLSLWaveSizeAttr>()) { 3420f77bdd2SXiang Li const StringRef WaveSizeKindStr = "hlsl.wavesize"; 3430f77bdd2SXiang Li std::string WaveSizeStr = 3440f77bdd2SXiang Li formatv("{0},{1},{2}", WaveSizeAttr->getMin(), WaveSizeAttr->getMax(), 3450f77bdd2SXiang Li WaveSizeAttr->getPreferred()); 3460f77bdd2SXiang Li Fn->addFnAttr(WaveSizeKindStr, WaveSizeStr); 3470f77bdd2SXiang Li } 348b89bb777SThurston Dang Fn->addFnAttr(llvm::Attribute::NoInline); 349906e41f4SXiang Li } 35022c477f9SChris Bieneman 35114ae5d2bSXiang Li static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) { 35214ae5d2bSXiang Li if (const auto *VT = dyn_cast<FixedVectorType>(Ty)) { 35314ae5d2bSXiang Li Value *Result = PoisonValue::get(Ty); 35414ae5d2bSXiang Li for (unsigned I = 0; I < VT->getNumElements(); ++I) { 35514ae5d2bSXiang Li Value *Elt = B.CreateCall(F, {B.getInt32(I)}); 35614ae5d2bSXiang Li Result = B.CreateInsertElement(Result, Elt, I); 35714ae5d2bSXiang Li } 35814ae5d2bSXiang Li return Result; 35914ae5d2bSXiang Li } 36014ae5d2bSXiang Li return B.CreateCall(F, {B.getInt32(0)}); 36114ae5d2bSXiang Li } 36214ae5d2bSXiang Li 36322c477f9SChris Bieneman llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B, 36414ae5d2bSXiang Li const ParmVarDecl &D, 36514ae5d2bSXiang Li llvm::Type *Ty) { 36622c477f9SChris Bieneman assert(D.hasAttrs() && "Entry parameter missing annotation attribute!"); 36722c477f9SChris Bieneman if (D.hasAttr<HLSLSV_GroupIndexAttr>()) { 36822c477f9SChris Bieneman llvm::Function *DxGroupIndex = 36922c477f9SChris Bieneman CGM.getIntrinsic(Intrinsic::dx_flattened_thread_id_in_group); 37022c477f9SChris Bieneman return B.CreateCall(FunctionCallee(DxGroupIndex)); 37122c477f9SChris Bieneman } 37214ae5d2bSXiang Li if (D.hasAttr<HLSLSV_DispatchThreadIDAttr>()) { 3731cb64d75SFarzon Lotfi llvm::Function *ThreadIDIntrinsic = 3741cb64d75SFarzon Lotfi CGM.getIntrinsic(getThreadIdIntrinsic()); 3756325dd57SNatalie Chouinard return buildVectorInput(B, ThreadIDIntrinsic, Ty); 37614ae5d2bSXiang Li } 377951a284fSZhengxing li if (D.hasAttr<HLSLSV_GroupThreadIDAttr>()) { 378951a284fSZhengxing li llvm::Function *GroupThreadIDIntrinsic = 379951a284fSZhengxing li CGM.getIntrinsic(getGroupThreadIdIntrinsic()); 380951a284fSZhengxing li return buildVectorInput(B, GroupThreadIDIntrinsic, Ty); 381951a284fSZhengxing li } 3825fd4f32fSZhengxing li if (D.hasAttr<HLSLSV_GroupIDAttr>()) { 3837a761100SZhengxing li llvm::Function *GroupIDIntrinsic = CGM.getIntrinsic(getGroupIdIntrinsic()); 3845fd4f32fSZhengxing li return buildVectorInput(B, GroupIDIntrinsic, Ty); 3855fd4f32fSZhengxing li } 38622c477f9SChris Bieneman assert(false && "Unhandled parameter attribute"); 38722c477f9SChris Bieneman return nullptr; 38822c477f9SChris Bieneman } 38922c477f9SChris Bieneman 39022c477f9SChris Bieneman void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, 39122c477f9SChris Bieneman llvm::Function *Fn) { 39222c477f9SChris Bieneman llvm::Module &M = CGM.getModule(); 39322c477f9SChris Bieneman llvm::LLVMContext &Ctx = M.getContext(); 39422c477f9SChris Bieneman auto *EntryTy = llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx), false); 39522c477f9SChris Bieneman Function *EntryFn = 39622c477f9SChris Bieneman Function::Create(EntryTy, Function::ExternalLinkage, FD->getName(), &M); 39722c477f9SChris Bieneman 39822c477f9SChris Bieneman // Copy function attributes over, we have no argument or return attributes 39922c477f9SChris Bieneman // that can be valid on the real entry. 40022c477f9SChris Bieneman AttributeList NewAttrs = AttributeList::get(Ctx, AttributeList::FunctionIndex, 40122c477f9SChris Bieneman Fn->getAttributes().getFnAttrs()); 40222c477f9SChris Bieneman EntryFn->setAttributes(NewAttrs); 40322c477f9SChris Bieneman setHLSLEntryAttributes(FD, EntryFn); 40422c477f9SChris Bieneman 40522c477f9SChris Bieneman // Set the called function as internal linkage. 40622c477f9SChris Bieneman Fn->setLinkage(GlobalValue::InternalLinkage); 40722c477f9SChris Bieneman 40822c477f9SChris Bieneman BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn); 40922c477f9SChris Bieneman IRBuilder<> B(BB); 41022c477f9SChris Bieneman llvm::SmallVector<Value *> Args; 41198e3075dSSteven Perron 41298e3075dSSteven Perron SmallVector<OperandBundleDef, 1> OB; 41398e3075dSSteven Perron if (CGM.shouldEmitConvergenceTokens()) { 41498e3075dSSteven Perron assert(EntryFn->isConvergent()); 41598e3075dSSteven Perron llvm::Value *I = B.CreateIntrinsic( 41698e3075dSSteven Perron llvm::Intrinsic::experimental_convergence_entry, {}, {}); 41798e3075dSSteven Perron llvm::Value *bundleArgs[] = {I}; 41898e3075dSSteven Perron OB.emplace_back("convergencectrl", bundleArgs); 41998e3075dSSteven Perron } 42098e3075dSSteven Perron 42122c477f9SChris Bieneman // FIXME: support struct parameters where semantics are on members. 422bc97751aSChris Bieneman // See: https://github.com/llvm/llvm-project/issues/57874 42314ae5d2bSXiang Li unsigned SRetOffset = 0; 42414ae5d2bSXiang Li for (const auto &Param : Fn->args()) { 42514ae5d2bSXiang Li if (Param.hasStructRetAttr()) { 42614ae5d2bSXiang Li // FIXME: support output. 42714ae5d2bSXiang Li // See: https://github.com/llvm/llvm-project/issues/57874 42814ae5d2bSXiang Li SRetOffset = 1; 42914ae5d2bSXiang Li Args.emplace_back(PoisonValue::get(Param.getType())); 43014ae5d2bSXiang Li continue; 43114ae5d2bSXiang Li } 43214ae5d2bSXiang Li const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); 43314ae5d2bSXiang Li Args.push_back(emitInputSemantic(B, *PD, Param.getType())); 43422c477f9SChris Bieneman } 43522c477f9SChris Bieneman 43698e3075dSSteven Perron CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB); 437be6b4f69SSteven Perron CI->setCallingConv(Fn->getCallingConv()); 43822c477f9SChris Bieneman // FIXME: Handle codegen for return type semantics. 439bc97751aSChris Bieneman // See: https://github.com/llvm/llvm-project/issues/57875 44022c477f9SChris Bieneman B.CreateRetVoid(); 441906e41f4SXiang Li } 442d3c54a17SChris Bieneman 4432c8bd4a7SHelena Kotas void CGHLSLRuntime::setHLSLFunctionAttributes(const FunctionDecl *FD, 4442c8bd4a7SHelena Kotas llvm::Function *Fn) { 4452c8bd4a7SHelena Kotas if (FD->isInExportDeclContext()) { 4462c8bd4a7SHelena Kotas const StringRef ExportAttrKindStr = "hlsl.export"; 4472c8bd4a7SHelena Kotas Fn->addFnAttr(ExportAttrKindStr); 4482c8bd4a7SHelena Kotas } 4492c8bd4a7SHelena Kotas } 4502c8bd4a7SHelena Kotas 451a8a49923SChris Bieneman static void gatherFunctions(SmallVectorImpl<Function *> &Fns, llvm::Module &M, 452a8a49923SChris Bieneman bool CtorOrDtor) { 453a8a49923SChris Bieneman const auto *GV = 454a8a49923SChris Bieneman M.getNamedGlobal(CtorOrDtor ? "llvm.global_ctors" : "llvm.global_dtors"); 455a8a49923SChris Bieneman if (!GV) 456d3c54a17SChris Bieneman return; 457a8a49923SChris Bieneman const auto *CA = dyn_cast<ConstantArray>(GV->getInitializer()); 458d3c54a17SChris Bieneman if (!CA) 459d3c54a17SChris Bieneman return; 460d3c54a17SChris Bieneman // The global_ctor array elements are a struct [Priority, Fn *, COMDat]. 461d3c54a17SChris Bieneman // HLSL neither supports priorities or COMDat values, so we will check those 462d3c54a17SChris Bieneman // in an assert but not handle them. 463d3c54a17SChris Bieneman 464d3c54a17SChris Bieneman llvm::SmallVector<Function *> CtorFns; 465d3c54a17SChris Bieneman for (const auto &Ctor : CA->operands()) { 466d3c54a17SChris Bieneman if (isa<ConstantAggregateZero>(Ctor)) 467d3c54a17SChris Bieneman continue; 468d3c54a17SChris Bieneman ConstantStruct *CS = cast<ConstantStruct>(Ctor); 469d3c54a17SChris Bieneman 470d3c54a17SChris Bieneman assert(cast<ConstantInt>(CS->getOperand(0))->getValue() == 65535 && 471d3c54a17SChris Bieneman "HLSL doesn't support setting priority for global ctors."); 472d3c54a17SChris Bieneman assert(isa<ConstantPointerNull>(CS->getOperand(2)) && 473d3c54a17SChris Bieneman "HLSL doesn't support COMDat for global ctors."); 474a8a49923SChris Bieneman Fns.push_back(cast<Function>(CS->getOperand(1))); 475d3c54a17SChris Bieneman } 476a8a49923SChris Bieneman } 477a8a49923SChris Bieneman 478a8a49923SChris Bieneman void CGHLSLRuntime::generateGlobalCtorDtorCalls() { 479a8a49923SChris Bieneman llvm::Module &M = CGM.getModule(); 480a8a49923SChris Bieneman SmallVector<Function *> CtorFns; 481a8a49923SChris Bieneman SmallVector<Function *> DtorFns; 482a8a49923SChris Bieneman gatherFunctions(CtorFns, M, true); 483a8a49923SChris Bieneman gatherFunctions(DtorFns, M, false); 484d3c54a17SChris Bieneman 485d3c54a17SChris Bieneman // Insert a call to the global constructor at the beginning of the entry block 486d3c54a17SChris Bieneman // to externally exported functions. This is a bit of a hack, but HLSL allows 487d3c54a17SChris Bieneman // global constructors, but doesn't support driver initialization of globals. 488d3c54a17SChris Bieneman for (auto &F : M.functions()) { 489d3c54a17SChris Bieneman if (!F.hasFnAttribute("hlsl.shader")) 490d3c54a17SChris Bieneman continue; 49198e3075dSSteven Perron auto *Token = getConvergenceToken(F.getEntryBlock()); 49298e3075dSSteven Perron Instruction *IP = &*F.getEntryBlock().begin(); 49398e3075dSSteven Perron SmallVector<OperandBundleDef, 1> OB; 49498e3075dSSteven Perron if (Token) { 49598e3075dSSteven Perron llvm::Value *bundleArgs[] = {Token}; 49698e3075dSSteven Perron OB.emplace_back("convergencectrl", bundleArgs); 49798e3075dSSteven Perron IP = Token->getNextNode(); 49898e3075dSSteven Perron } 49998e3075dSSteven Perron IRBuilder<> B(IP); 50052b07d97SNathan Gauër for (auto *Fn : CtorFns) { 50152b07d97SNathan Gauër auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); 50252b07d97SNathan Gauër CI->setCallingConv(Fn->getCallingConv()); 50352b07d97SNathan Gauër } 504a8a49923SChris Bieneman 505a8a49923SChris Bieneman // Insert global dtors before the terminator of the last instruction 506a8a49923SChris Bieneman B.SetInsertPoint(F.back().getTerminator()); 50752b07d97SNathan Gauër for (auto *Fn : DtorFns) { 50852b07d97SNathan Gauër auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); 50952b07d97SNathan Gauër CI->setCallingConv(Fn->getCallingConv()); 51052b07d97SNathan Gauër } 511d3c54a17SChris Bieneman } 5122bdfececSXiang Li 5132bdfececSXiang Li // No need to keep global ctors/dtors for non-lib profile after call to 5142bdfececSXiang Li // ctors/dtors added for entry. 5152bdfececSXiang Li Triple T(M.getTargetTriple()); 5162bdfececSXiang Li if (T.getEnvironment() != Triple::EnvironmentType::Library) { 5172bdfececSXiang Li if (auto *GV = M.getNamedGlobal("llvm.global_ctors")) 5182bdfececSXiang Li GV->eraseFromParent(); 5192bdfececSXiang Li if (auto *GV = M.getNamedGlobal("llvm.global_dtors")) 5202bdfececSXiang Li GV->eraseFromParent(); 5212bdfececSXiang Li } 522d3c54a17SChris Bieneman } 5237dbfa7b9SHelena Kotas 524719f0d92SHelena Kotas // Returns true if the type is an HLSL resource class 525719f0d92SHelena Kotas static bool isResourceRecordType(const clang::Type *Ty) { 526719f0d92SHelena Kotas return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; 5277dbfa7b9SHelena Kotas } 5287dbfa7b9SHelena Kotas 529719f0d92SHelena Kotas static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, 530719f0d92SHelena Kotas llvm::GlobalVariable *GV, unsigned Slot, 531719f0d92SHelena Kotas unsigned Space) { 5327dbfa7b9SHelena Kotas LLVMContext &Ctx = CGM.getLLVMContext(); 5337dbfa7b9SHelena Kotas llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx); 5347dbfa7b9SHelena Kotas 535719f0d92SHelena Kotas llvm::Function *InitResFunc = llvm::Function::Create( 536719f0d92SHelena Kotas llvm::FunctionType::get(CGM.VoidTy, false), 5377dbfa7b9SHelena Kotas llvm::GlobalValue::InternalLinkage, 538719f0d92SHelena Kotas ("_init_resource_" + VD->getName()).str(), CGM.getModule()); 539719f0d92SHelena Kotas InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline); 5407dbfa7b9SHelena Kotas 5417dbfa7b9SHelena Kotas llvm::BasicBlock *EntryBB = 542719f0d92SHelena Kotas llvm::BasicBlock::Create(Ctx, "entry", InitResFunc); 5437dbfa7b9SHelena Kotas CGBuilderTy Builder(CGM, Ctx); 5447dbfa7b9SHelena Kotas const DataLayout &DL = CGM.getModule().getDataLayout(); 5457dbfa7b9SHelena Kotas Builder.SetInsertPoint(EntryBB); 5467dbfa7b9SHelena Kotas 5477dbfa7b9SHelena Kotas const HLSLAttributedResourceType *AttrResType = 5487dbfa7b9SHelena Kotas HLSLAttributedResourceType::findHandleTypeOnResource( 5497dbfa7b9SHelena Kotas VD->getType().getTypePtr()); 5507dbfa7b9SHelena Kotas 5517dbfa7b9SHelena Kotas // FIXME: Only simple declarations of resources are supported for now. 5527dbfa7b9SHelena Kotas // Arrays of resources or resources in user defined classes are 5537dbfa7b9SHelena Kotas // not implemented yet. 5547dbfa7b9SHelena Kotas assert(AttrResType != nullptr && 5557dbfa7b9SHelena Kotas "Resource class must have a handle of HLSLAttributedResourceType"); 5567dbfa7b9SHelena Kotas 5577dbfa7b9SHelena Kotas llvm::Type *TargetTy = 5587dbfa7b9SHelena Kotas CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType); 5597dbfa7b9SHelena Kotas assert(TargetTy != nullptr && 5607dbfa7b9SHelena Kotas "Failed to convert resource handle to target type"); 5617dbfa7b9SHelena Kotas 562719f0d92SHelena Kotas llvm::Value *Args[] = { 563719f0d92SHelena Kotas llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */ 564719f0d92SHelena Kotas llvm::ConstantInt::get(CGM.IntTy, Slot), /* lower_bound */ 5657dbfa7b9SHelena Kotas // FIXME: resource arrays are not yet implemented 566719f0d92SHelena Kotas llvm::ConstantInt::get(CGM.IntTy, 1), /* range_size */ 567719f0d92SHelena Kotas llvm::ConstantInt::get(CGM.IntTy, 0), /* index */ 5687dbfa7b9SHelena Kotas // FIXME: NonUniformResourceIndex bit is not yet implemented 569719f0d92SHelena Kotas llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */ 570719f0d92SHelena Kotas }; 5717dbfa7b9SHelena Kotas llvm::Value *CreateHandle = Builder.CreateIntrinsic( 572719f0d92SHelena Kotas /*ReturnType=*/TargetTy, 573719f0d92SHelena Kotas CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr, 574719f0d92SHelena Kotas Twine(VD->getName()).concat("_h")); 5757dbfa7b9SHelena Kotas 576719f0d92SHelena Kotas llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0); 5777dbfa7b9SHelena Kotas Builder.CreateAlignedStore(CreateHandle, HandleRef, 5787dbfa7b9SHelena Kotas HandleRef->getPointerAlignment(DL)); 579719f0d92SHelena Kotas Builder.CreateRetVoid(); 580719f0d92SHelena Kotas 581719f0d92SHelena Kotas CGM.AddCXXGlobalInit(InitResFunc); 5827dbfa7b9SHelena Kotas } 5837dbfa7b9SHelena Kotas 584719f0d92SHelena Kotas void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, 585719f0d92SHelena Kotas llvm::GlobalVariable *GV) { 586719f0d92SHelena Kotas 587719f0d92SHelena Kotas // If the global variable has resource binding, create an init function 588719f0d92SHelena Kotas // for the resource 589719f0d92SHelena Kotas const HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>(); 590719f0d92SHelena Kotas if (!RBA) 591719f0d92SHelena Kotas // FIXME: collect unbound resources for implicit binding resolution later 592719f0d92SHelena Kotas // on? 593719f0d92SHelena Kotas return; 594719f0d92SHelena Kotas 595719f0d92SHelena Kotas if (!isResourceRecordType(VD->getType().getTypePtr())) 596719f0d92SHelena Kotas // FIXME: Only simple declarations of resources are supported for now. 597719f0d92SHelena Kotas // Arrays of resources or resources in user defined classes are 598719f0d92SHelena Kotas // not implemented yet. 599719f0d92SHelena Kotas return; 600719f0d92SHelena Kotas 601719f0d92SHelena Kotas createResourceInitFn(CGM, VD, GV, RBA->getSlotNumber(), 602719f0d92SHelena Kotas RBA->getSpaceNumber()); 6037dbfa7b9SHelena Kotas } 60498e3075dSSteven Perron 60598e3075dSSteven Perron llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { 60698e3075dSSteven Perron if (!CGM.shouldEmitConvergenceTokens()) 60798e3075dSSteven Perron return nullptr; 60898e3075dSSteven Perron 60998e3075dSSteven Perron auto E = BB.end(); 61098e3075dSSteven Perron for (auto I = BB.begin(); I != E; ++I) { 61198e3075dSSteven Perron auto *II = dyn_cast<llvm::IntrinsicInst>(&*I); 61298e3075dSSteven Perron if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) { 61398e3075dSSteven Perron return II; 61498e3075dSSteven Perron } 61598e3075dSSteven Perron } 61698e3075dSSteven Perron llvm_unreachable("Convergence token should have been emitted."); 61798e3075dSSteven Perron return nullptr; 61898e3075dSSteven Perron } 619