1 //===----- CGHLSLRuntime.cpp - Interface to HLSL Runtimes -----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This provides an abstract class for HLSL code generation. Concrete 10 // subclasses of this implement code generation for specific HLSL 11 // runtime libraries. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "CGHLSLRuntime.h" 16 #include "CGDebugInfo.h" 17 #include "CodeGenFunction.h" 18 #include "CodeGenModule.h" 19 #include "TargetInfo.h" 20 #include "clang/AST/ASTContext.h" 21 #include "clang/AST/Decl.h" 22 #include "clang/AST/RecursiveASTVisitor.h" 23 #include "clang/AST/Type.h" 24 #include "clang/Basic/TargetOptions.h" 25 #include "llvm/ADT/SmallVector.h" 26 #include "llvm/Frontend/HLSL/RootSignatureMetadata.h" 27 #include "llvm/IR/Constants.h" 28 #include "llvm/IR/DerivedTypes.h" 29 #include "llvm/IR/GlobalVariable.h" 30 #include "llvm/IR/LLVMContext.h" 31 #include "llvm/IR/Metadata.h" 32 #include "llvm/IR/Module.h" 33 #include "llvm/IR/Type.h" 34 #include "llvm/IR/Value.h" 35 #include "llvm/Support/Alignment.h" 36 #include "llvm/Support/ErrorHandling.h" 37 #include "llvm/Support/FormatVariadic.h" 38 39 using namespace clang; 40 using namespace CodeGen; 41 using namespace clang::hlsl; 42 using namespace llvm; 43 44 using llvm::hlsl::CBufferRowSizeInBytes; 45 46 namespace { 47 48 void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { 49 // The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs. 50 // Assume ValVersionStr is legal here. 51 VersionTuple Version; 52 if (Version.tryParse(ValVersionStr) || Version.getBuild() || 53 Version.getSubminor() || !Version.getMinor()) { 54 return; 55 } 56 57 uint64_t Major = Version.getMajor(); 58 uint64_t Minor = *Version.getMinor(); 59 60 auto &Ctx = M.getContext(); 61 IRBuilder<> B(M.getContext()); 62 MDNode *Val = MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(Major)), 63 ConstantAsMetadata::get(B.getInt32(Minor))}); 64 StringRef DXILValKey = "dx.valver"; 65 auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey); 66 DXILValMD->addOperand(Val); 67 } 68 69 void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer, 70 ArrayRef<llvm::hlsl::rootsig::RootElement> Elements, 71 llvm::Function *Fn, llvm::Module &M) { 72 auto &Ctx = M.getContext(); 73 74 llvm::hlsl::rootsig::MetadataBuilder RSBuilder(Ctx, Elements); 75 MDNode *RootSignature = RSBuilder.BuildRootSignature(); 76 77 ConstantAsMetadata *Version = ConstantAsMetadata::get(ConstantInt::get( 78 llvm::Type::getInt32Ty(Ctx), llvm::to_underlying(RootSigVer))); 79 MDNode *MDVals = 80 MDNode::get(Ctx, {ValueAsMetadata::get(Fn), RootSignature, Version}); 81 82 StringRef RootSignatureValKey = "dx.rootsignatures"; 83 auto *RootSignatureValMD = M.getOrInsertNamedMetadata(RootSignatureValKey); 84 RootSignatureValMD->addOperand(MDVals); 85 } 86 87 } // namespace 88 89 llvm::Type * 90 CGHLSLRuntime::convertHLSLSpecificType(const Type *T, 91 SmallVector<int32_t> *Packoffsets) { 92 assert(T->isHLSLSpecificType() && "Not an HLSL specific type!"); 93 94 // Check if the target has a specific translation for this type first. 95 if (llvm::Type *TargetTy = 96 CGM.getTargetCodeGenInfo().getHLSLType(CGM, T, Packoffsets)) 97 return TargetTy; 98 99 llvm_unreachable("Generic handling of HLSL types is not supported."); 100 } 101 102 llvm::Triple::ArchType CGHLSLRuntime::getArch() { 103 return CGM.getTarget().getTriple().getArch(); 104 } 105 106 // Returns true if the type is an HLSL resource class or an array of them 107 static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { 108 while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) 109 Ty = CAT->getArrayElementTypeNoTypeQual(); 110 return Ty->isHLSLResourceRecord(); 111 } 112 113 // Emits constant global variables for buffer constants declarations 114 // and creates metadata linking the constant globals with the buffer global. 115 void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, 116 llvm::GlobalVariable *BufGV) { 117 LLVMContext &Ctx = CGM.getLLVMContext(); 118 119 // get the layout struct from constant buffer target type 120 llvm::Type *BufType = BufGV->getValueType(); 121 llvm::Type *BufLayoutType = 122 cast<llvm::TargetExtType>(BufType)->getTypeParameter(0); 123 llvm::StructType *LayoutStruct = cast<llvm::StructType>( 124 cast<llvm::TargetExtType>(BufLayoutType)->getTypeParameter(0)); 125 126 // Start metadata list associating the buffer global variable with its 127 // constatns 128 SmallVector<llvm::Metadata *> BufGlobals; 129 BufGlobals.push_back(ValueAsMetadata::get(BufGV)); 130 131 const auto *ElemIt = LayoutStruct->element_begin(); 132 for (Decl *D : BufDecl->buffer_decls()) { 133 if (isa<CXXRecordDecl, EmptyDecl>(D)) 134 // Nothing to do for this declaration. 135 continue; 136 if (isa<FunctionDecl>(D)) { 137 // A function within an cbuffer is effectively a top-level function. 138 CGM.EmitTopLevelDecl(D); 139 continue; 140 } 141 VarDecl *VD = dyn_cast<VarDecl>(D); 142 if (!VD) 143 continue; 144 145 QualType VDTy = VD->getType(); 146 if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { 147 if (VD->getStorageClass() == SC_Static || 148 VDTy.getAddressSpace() == LangAS::hlsl_groupshared || 149 isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { 150 // Emit static and groupshared variables and resource classes inside 151 // cbuffer as regular globals 152 CGM.EmitGlobal(VD); 153 } else { 154 // Anything else that is not in the hlsl_constant address space must be 155 // an empty struct or a zero-sized array and can be ignored 156 assert(BufDecl->getASTContext().getTypeSize(VDTy) == 0 && 157 "constant buffer decl with non-zero sized type outside of " 158 "hlsl_constant address space"); 159 } 160 continue; 161 } 162 163 assert(ElemIt != LayoutStruct->element_end() && 164 "number of elements in layout struct does not match"); 165 llvm::Type *LayoutType = *ElemIt++; 166 167 // FIXME: handle resources inside user defined structs 168 // (llvm/wg-hlsl#175) 169 170 // create global variable for the constant and to metadata list 171 GlobalVariable *ElemGV = 172 cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(VD, LayoutType)); 173 BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); 174 } 175 assert(ElemIt == LayoutStruct->element_end() && 176 "number of elements in layout struct does not match"); 177 178 // add buffer metadata to the module 179 CGM.getModule() 180 .getOrInsertNamedMetadata("hlsl.cbs") 181 ->addOperand(MDNode::get(Ctx, BufGlobals)); 182 } 183 184 // Creates resource handle type for the HLSL buffer declaration 185 static const clang::HLSLAttributedResourceType * 186 createBufferHandleType(const HLSLBufferDecl *BufDecl) { 187 ASTContext &AST = BufDecl->getASTContext(); 188 QualType QT = AST.getHLSLAttributedResourceType( 189 AST.HLSLResourceTy, 190 QualType(BufDecl->getLayoutStruct()->getTypeForDecl(), 0), 191 HLSLAttributedResourceType::Attributes(ResourceClass::CBuffer)); 192 return cast<HLSLAttributedResourceType>(QT.getTypePtr()); 193 } 194 195 // Iterates over all declarations in the HLSL buffer and based on the 196 // packoffset or register(c#) annotations it fills outs the Layout 197 // vector with the user-specified layout offsets. 198 // The buffer offsets can be specified 2 ways: 199 // 1. declarations in cbuffer {} block can have a packoffset annotation 200 // (translates to HLSLPackOffsetAttr) 201 // 2. default constant buffer declarations at global scope can have 202 // register(c#) annotations (translates to HLSLResourceBindingAttr with 203 // RegisterType::C) 204 // It is not guaranteed that all declarations in a buffer have an annotation. 205 // For those where it is not specified a -1 value is added to the Layout 206 // vector. In the final layout these declarations will be placed at the end 207 // of the HLSL buffer after all of the elements with specified offset. 208 static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl, 209 SmallVector<int32_t> &Layout) { 210 assert(Layout.empty() && "expected empty vector for layout"); 211 assert(BufDecl->hasValidPackoffset()); 212 213 for (Decl *D : BufDecl->buffer_decls()) { 214 if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) { 215 continue; 216 } 217 VarDecl *VD = dyn_cast<VarDecl>(D); 218 if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) 219 continue; 220 221 if (!VD->hasAttrs()) { 222 Layout.push_back(-1); 223 continue; 224 } 225 226 int32_t Offset = -1; 227 for (auto *Attr : VD->getAttrs()) { 228 if (auto *POA = dyn_cast<HLSLPackOffsetAttr>(Attr)) { 229 Offset = POA->getOffsetInBytes(); 230 break; 231 } 232 auto *RBA = dyn_cast<HLSLResourceBindingAttr>(Attr); 233 if (RBA && 234 RBA->getRegisterType() == HLSLResourceBindingAttr::RegisterType::C) { 235 Offset = RBA->getSlotNumber() * CBufferRowSizeInBytes; 236 break; 237 } 238 } 239 Layout.push_back(Offset); 240 } 241 } 242 243 // Codegen for HLSLBufferDecl 244 void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { 245 246 assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet"); 247 248 // create resource handle type for the buffer 249 const clang::HLSLAttributedResourceType *ResHandleTy = 250 createBufferHandleType(BufDecl); 251 252 // empty constant buffer is ignored 253 if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty()) 254 return; 255 256 // create global variable for the constant buffer 257 SmallVector<int32_t> Layout; 258 if (BufDecl->hasValidPackoffset()) 259 fillPackoffsetLayout(BufDecl, Layout); 260 261 llvm::TargetExtType *TargetTy = 262 cast<llvm::TargetExtType>(convertHLSLSpecificType( 263 ResHandleTy, BufDecl->hasValidPackoffset() ? &Layout : nullptr)); 264 llvm::GlobalVariable *BufGV = new GlobalVariable( 265 TargetTy, /*isConstant*/ false, 266 GlobalValue::LinkageTypes::ExternalLinkage, PoisonValue::get(TargetTy), 267 llvm::formatv("{0}{1}", BufDecl->getName(), 268 BufDecl->isCBuffer() ? ".cb" : ".tb"), 269 GlobalValue::NotThreadLocal); 270 CGM.getModule().insertGlobalVariable(BufGV); 271 272 // Add globals for constant buffer elements and create metadata nodes 273 emitBufferGlobalsAndMetadata(BufDecl, BufGV); 274 275 // Initialize cbuffer from binding (implicit or explicit) 276 HLSLResourceBindingAttr *RBA = BufDecl->getAttr<HLSLResourceBindingAttr>(); 277 assert(RBA && 278 "cbuffer/tbuffer should always have resource binding attribute"); 279 initializeBufferFromBinding(BufDecl, BufGV, RBA); 280 } 281 282 llvm::TargetExtType * 283 CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { 284 const auto Entry = LayoutTypes.find(StructType); 285 if (Entry != LayoutTypes.end()) 286 return Entry->getSecond(); 287 return nullptr; 288 } 289 290 void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType, 291 llvm::TargetExtType *LayoutTy) { 292 assert(getHLSLBufferLayoutType(StructType) == nullptr && 293 "layout type for this struct already exist"); 294 LayoutTypes[StructType] = LayoutTy; 295 } 296 297 void CGHLSLRuntime::finishCodeGen() { 298 auto &TargetOpts = CGM.getTarget().getTargetOpts(); 299 auto &CodeGenOpts = CGM.getCodeGenOpts(); 300 auto &LangOpts = CGM.getLangOpts(); 301 llvm::Module &M = CGM.getModule(); 302 Triple T(M.getTargetTriple()); 303 if (T.getArch() == Triple::ArchType::dxil) 304 addDxilValVersion(TargetOpts.DxilValidatorVersion, M); 305 if (CodeGenOpts.ResMayAlias) 306 M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.resmayalias", 1); 307 308 // NativeHalfType corresponds to the -fnative-half-type clang option which is 309 // aliased by clang-dxc's -enable-16bit-types option. This option is used to 310 // set the UseNativeLowPrecision DXIL module flag in the DirectX backend 311 if (LangOpts.NativeHalfType) 312 M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.nativelowprec", 313 1); 314 315 generateGlobalCtorDtorCalls(); 316 } 317 318 void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes( 319 const FunctionDecl *FD, llvm::Function *Fn) { 320 const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>(); 321 assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr"); 322 const StringRef ShaderAttrKindStr = "hlsl.shader"; 323 Fn->addFnAttr(ShaderAttrKindStr, 324 llvm::Triple::getEnvironmentTypeName(ShaderAttr->getType())); 325 if (HLSLNumThreadsAttr *NumThreadsAttr = FD->getAttr<HLSLNumThreadsAttr>()) { 326 const StringRef NumThreadsKindStr = "hlsl.numthreads"; 327 std::string NumThreadsStr = 328 formatv("{0},{1},{2}", NumThreadsAttr->getX(), NumThreadsAttr->getY(), 329 NumThreadsAttr->getZ()); 330 Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr); 331 } 332 if (HLSLWaveSizeAttr *WaveSizeAttr = FD->getAttr<HLSLWaveSizeAttr>()) { 333 const StringRef WaveSizeKindStr = "hlsl.wavesize"; 334 std::string WaveSizeStr = 335 formatv("{0},{1},{2}", WaveSizeAttr->getMin(), WaveSizeAttr->getMax(), 336 WaveSizeAttr->getPreferred()); 337 Fn->addFnAttr(WaveSizeKindStr, WaveSizeStr); 338 } 339 // HLSL entry functions are materialized for module functions with 340 // HLSLShaderAttr attribute. SetLLVMFunctionAttributesForDefinition called 341 // later in the compiler-flow for such module functions is not aware of and 342 // hence not able to set attributes of the newly materialized entry functions. 343 // So, set attributes of entry function here, as appropriate. 344 if (CGM.getCodeGenOpts().OptimizationLevel == 0) 345 Fn->addFnAttr(llvm::Attribute::OptimizeNone); 346 Fn->addFnAttr(llvm::Attribute::NoInline); 347 } 348 349 static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) { 350 if (const auto *VT = dyn_cast<FixedVectorType>(Ty)) { 351 Value *Result = PoisonValue::get(Ty); 352 for (unsigned I = 0; I < VT->getNumElements(); ++I) { 353 Value *Elt = B.CreateCall(F, {B.getInt32(I)}); 354 Result = B.CreateInsertElement(Result, Elt, I); 355 } 356 return Result; 357 } 358 return B.CreateCall(F, {B.getInt32(0)}); 359 } 360 361 static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV, 362 unsigned BuiltIn) { 363 LLVMContext &Ctx = GV->getContext(); 364 IRBuilder<> B(GV->getContext()); 365 MDNode *Operands = MDNode::get( 366 Ctx, 367 {ConstantAsMetadata::get(B.getInt32(/* Spirv::Decoration::BuiltIn */ 11)), 368 ConstantAsMetadata::get(B.getInt32(BuiltIn))}); 369 MDNode *Decoration = MDNode::get(Ctx, {Operands}); 370 GV->addMetadata("spirv.Decorations", *Decoration); 371 } 372 373 static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M, 374 llvm::Type *Ty, const Twine &Name, 375 unsigned BuiltInID) { 376 auto *GV = new llvm::GlobalVariable( 377 M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage, 378 /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr, 379 llvm::GlobalVariable::GeneralDynamicTLSModel, 380 /* AddressSpace */ 7, /* isExternallyInitialized= */ true); 381 addSPIRVBuiltinDecoration(GV, BuiltInID); 382 GV->setVisibility(llvm::GlobalValue::HiddenVisibility); 383 return B.CreateLoad(Ty, GV); 384 } 385 386 llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B, 387 const ParmVarDecl &D, 388 llvm::Type *Ty) { 389 assert(D.hasAttrs() && "Entry parameter missing annotation attribute!"); 390 if (D.hasAttr<HLSLSV_GroupIndexAttr>()) { 391 llvm::Function *GroupIndex = 392 CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic()); 393 return B.CreateCall(FunctionCallee(GroupIndex)); 394 } 395 if (D.hasAttr<HLSLSV_DispatchThreadIDAttr>()) { 396 llvm::Intrinsic::ID IntrinID = getThreadIdIntrinsic(); 397 llvm::Function *ThreadIDIntrinsic = 398 llvm::Intrinsic::isOverloaded(IntrinID) 399 ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) 400 : CGM.getIntrinsic(IntrinID); 401 return buildVectorInput(B, ThreadIDIntrinsic, Ty); 402 } 403 if (D.hasAttr<HLSLSV_GroupThreadIDAttr>()) { 404 llvm::Intrinsic::ID IntrinID = getGroupThreadIdIntrinsic(); 405 llvm::Function *GroupThreadIDIntrinsic = 406 llvm::Intrinsic::isOverloaded(IntrinID) 407 ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) 408 : CGM.getIntrinsic(IntrinID); 409 return buildVectorInput(B, GroupThreadIDIntrinsic, Ty); 410 } 411 if (D.hasAttr<HLSLSV_GroupIDAttr>()) { 412 llvm::Intrinsic::ID IntrinID = getGroupIdIntrinsic(); 413 llvm::Function *GroupIDIntrinsic = 414 llvm::Intrinsic::isOverloaded(IntrinID) 415 ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) 416 : CGM.getIntrinsic(IntrinID); 417 return buildVectorInput(B, GroupIDIntrinsic, Ty); 418 } 419 if (D.hasAttr<HLSLSV_PositionAttr>()) { 420 if (getArch() == llvm::Triple::spirv) 421 return createSPIRVBuiltinLoad(B, CGM.getModule(), Ty, "sv_position", 422 /* BuiltIn::Position */ 0); 423 llvm_unreachable("SV_Position semantic not implemented for this target."); 424 } 425 assert(false && "Unhandled parameter attribute"); 426 return nullptr; 427 } 428 429 void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, 430 llvm::Function *Fn) { 431 llvm::Module &M = CGM.getModule(); 432 llvm::LLVMContext &Ctx = M.getContext(); 433 auto *EntryTy = llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx), false); 434 Function *EntryFn = 435 Function::Create(EntryTy, Function::ExternalLinkage, FD->getName(), &M); 436 437 // Copy function attributes over, we have no argument or return attributes 438 // that can be valid on the real entry. 439 AttributeList NewAttrs = AttributeList::get(Ctx, AttributeList::FunctionIndex, 440 Fn->getAttributes().getFnAttrs()); 441 EntryFn->setAttributes(NewAttrs); 442 setHLSLEntryAttributes(FD, EntryFn); 443 444 // Set the called function as internal linkage. 445 Fn->setLinkage(GlobalValue::InternalLinkage); 446 447 BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn); 448 IRBuilder<> B(BB); 449 llvm::SmallVector<Value *> Args; 450 451 SmallVector<OperandBundleDef, 1> OB; 452 if (CGM.shouldEmitConvergenceTokens()) { 453 assert(EntryFn->isConvergent()); 454 llvm::Value *I = 455 B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, {}); 456 llvm::Value *bundleArgs[] = {I}; 457 OB.emplace_back("convergencectrl", bundleArgs); 458 } 459 460 // FIXME: support struct parameters where semantics are on members. 461 // See: https://github.com/llvm/llvm-project/issues/57874 462 unsigned SRetOffset = 0; 463 for (const auto &Param : Fn->args()) { 464 if (Param.hasStructRetAttr()) { 465 // FIXME: support output. 466 // See: https://github.com/llvm/llvm-project/issues/57874 467 SRetOffset = 1; 468 Args.emplace_back(PoisonValue::get(Param.getType())); 469 continue; 470 } 471 const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); 472 Args.push_back(emitInputSemantic(B, *PD, Param.getType())); 473 } 474 475 CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB); 476 CI->setCallingConv(Fn->getCallingConv()); 477 // FIXME: Handle codegen for return type semantics. 478 // See: https://github.com/llvm/llvm-project/issues/57875 479 B.CreateRetVoid(); 480 481 // Add and identify root signature to function, if applicable 482 for (const Attr *Attr : FD->getAttrs()) { 483 if (const auto *RSAttr = dyn_cast<RootSignatureAttr>(Attr)) { 484 auto *RSDecl = RSAttr->getSignatureDecl(); 485 addRootSignature(RSDecl->getVersion(), RSDecl->getRootElements(), EntryFn, 486 M); 487 } 488 } 489 } 490 491 static void gatherFunctions(SmallVectorImpl<Function *> &Fns, llvm::Module &M, 492 bool CtorOrDtor) { 493 const auto *GV = 494 M.getNamedGlobal(CtorOrDtor ? "llvm.global_ctors" : "llvm.global_dtors"); 495 if (!GV) 496 return; 497 const auto *CA = dyn_cast<ConstantArray>(GV->getInitializer()); 498 if (!CA) 499 return; 500 // The global_ctor array elements are a struct [Priority, Fn *, COMDat]. 501 // HLSL neither supports priorities or COMDat values, so we will check those 502 // in an assert but not handle them. 503 504 for (const auto &Ctor : CA->operands()) { 505 if (isa<ConstantAggregateZero>(Ctor)) 506 continue; 507 ConstantStruct *CS = cast<ConstantStruct>(Ctor); 508 509 assert(cast<ConstantInt>(CS->getOperand(0))->getValue() == 65535 && 510 "HLSL doesn't support setting priority for global ctors."); 511 assert(isa<ConstantPointerNull>(CS->getOperand(2)) && 512 "HLSL doesn't support COMDat for global ctors."); 513 Fns.push_back(cast<Function>(CS->getOperand(1))); 514 } 515 } 516 517 void CGHLSLRuntime::generateGlobalCtorDtorCalls() { 518 llvm::Module &M = CGM.getModule(); 519 SmallVector<Function *> CtorFns; 520 SmallVector<Function *> DtorFns; 521 gatherFunctions(CtorFns, M, true); 522 gatherFunctions(DtorFns, M, false); 523 524 // Insert a call to the global constructor at the beginning of the entry block 525 // to externally exported functions. This is a bit of a hack, but HLSL allows 526 // global constructors, but doesn't support driver initialization of globals. 527 for (auto &F : M.functions()) { 528 if (!F.hasFnAttribute("hlsl.shader")) 529 continue; 530 auto *Token = getConvergenceToken(F.getEntryBlock()); 531 Instruction *IP = &*F.getEntryBlock().begin(); 532 SmallVector<OperandBundleDef, 1> OB; 533 if (Token) { 534 llvm::Value *bundleArgs[] = {Token}; 535 OB.emplace_back("convergencectrl", bundleArgs); 536 IP = Token->getNextNode(); 537 } 538 IRBuilder<> B(IP); 539 for (auto *Fn : CtorFns) { 540 auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); 541 CI->setCallingConv(Fn->getCallingConv()); 542 } 543 544 // Insert global dtors before the terminator of the last instruction 545 B.SetInsertPoint(F.back().getTerminator()); 546 for (auto *Fn : DtorFns) { 547 auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); 548 CI->setCallingConv(Fn->getCallingConv()); 549 } 550 } 551 552 // No need to keep global ctors/dtors for non-lib profile after call to 553 // ctors/dtors added for entry. 554 Triple T(M.getTargetTriple()); 555 if (T.getEnvironment() != Triple::EnvironmentType::Library) { 556 if (auto *GV = M.getNamedGlobal("llvm.global_ctors")) 557 GV->eraseFromParent(); 558 if (auto *GV = M.getNamedGlobal("llvm.global_dtors")) 559 GV->eraseFromParent(); 560 } 561 } 562 563 static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, 564 Intrinsic::ID IntrID, 565 ArrayRef<llvm::Value *> Args) { 566 567 LLVMContext &Ctx = CGM.getLLVMContext(); 568 llvm::Function *InitResFunc = llvm::Function::Create( 569 llvm::FunctionType::get(CGM.VoidTy, false), 570 llvm::GlobalValue::InternalLinkage, 571 ("_init_buffer_" + GV->getName()).str(), CGM.getModule()); 572 InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline); 573 574 llvm::BasicBlock *EntryBB = 575 llvm::BasicBlock::Create(Ctx, "entry", InitResFunc); 576 CGBuilderTy Builder(CGM, Ctx); 577 const DataLayout &DL = CGM.getModule().getDataLayout(); 578 Builder.SetInsertPoint(EntryBB); 579 580 // Make sure the global variable is buffer resource handle 581 llvm::Type *HandleTy = GV->getValueType(); 582 assert(HandleTy->isTargetExtTy() && "unexpected type of the buffer global"); 583 584 llvm::Value *CreateHandle = Builder.CreateIntrinsic( 585 /*ReturnType=*/HandleTy, IntrID, Args, nullptr, 586 Twine(GV->getName()).concat("_h")); 587 588 llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0); 589 Builder.CreateAlignedStore(CreateHandle, HandleRef, 590 HandleRef->getPointerAlignment(DL)); 591 Builder.CreateRetVoid(); 592 593 CGM.AddCXXGlobalInit(InitResFunc); 594 } 595 596 void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, 597 llvm::GlobalVariable *GV, 598 HLSLResourceBindingAttr *RBA) { 599 assert(RBA && "expect a nonnull binding attribute"); 600 llvm::Type *Int1Ty = llvm::Type::getInt1Ty(CGM.getLLVMContext()); 601 auto *NonUniform = llvm::ConstantInt::get(Int1Ty, false); 602 auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0); 603 auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1); 604 auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber()); 605 Value *Name = nullptr; 606 607 llvm::Intrinsic::ID IntrinsicID = 608 RBA->hasRegisterSlot() 609 ? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic() 610 : CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(); 611 612 std::string Str(BufDecl->getName()); 613 std::string GlobalName(Str + ".str"); 614 Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); 615 616 // buffer with explicit binding 617 if (RBA->hasRegisterSlot()) { 618 auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber()); 619 SmallVector<Value *> Args{Space, RegSlot, RangeSize, 620 Index, NonUniform, Name}; 621 initializeBuffer(CGM, GV, IntrinsicID, Args); 622 } else { 623 // buffer with implicit binding 624 auto *OrderID = 625 llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID()); 626 SmallVector<Value *> Args{OrderID, Space, RangeSize, 627 Index, NonUniform, Name}; 628 initializeBuffer(CGM, GV, IntrinsicID, Args); 629 } 630 } 631 632 void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, 633 llvm::GlobalVariable *GV) { 634 if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>()) 635 addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn()); 636 } 637 638 llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { 639 if (!CGM.shouldEmitConvergenceTokens()) 640 return nullptr; 641 642 auto E = BB.end(); 643 for (auto I = BB.begin(); I != E; ++I) { 644 auto *II = dyn_cast<llvm::IntrinsicInst>(&*I); 645 if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) { 646 return II; 647 } 648 } 649 llvm_unreachable("Convergence token should have been emitted."); 650 return nullptr; 651 } 652 653 class OpaqueValueVisitor : public RecursiveASTVisitor<OpaqueValueVisitor> { 654 public: 655 llvm::SmallPtrSet<OpaqueValueExpr *, 8> OVEs; 656 OpaqueValueVisitor() {} 657 658 bool VisitOpaqueValueExpr(OpaqueValueExpr *E) { 659 OVEs.insert(E); 660 return true; 661 } 662 }; 663 664 void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF, 665 InitListExpr *E) { 666 667 typedef CodeGenFunction::OpaqueValueMappingData OpaqueValueMappingData; 668 OpaqueValueVisitor Visitor; 669 Visitor.TraverseStmt(E); 670 for (auto *OVE : Visitor.OVEs) { 671 if (CGF.isOpaqueValueEmitted(OVE)) 672 continue; 673 if (OpaqueValueMappingData::shouldBindAsLValue(OVE)) { 674 LValue LV = CGF.EmitLValue(OVE->getSourceExpr()); 675 OpaqueValueMappingData::bind(CGF, OVE, LV); 676 } else { 677 RValue RV = CGF.EmitAnyExpr(OVE->getSourceExpr()); 678 OpaqueValueMappingData::bind(CGF, OVE, RV); 679 } 680 } 681 } 682