xref: /llvm-project/clang/lib/CodeGen/CGHLSLRuntime.cpp (revision d92bac8a3ebb19106f6bca6b7613a27c52cb48ab)
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