//===----- CGHLSLRuntime.cpp - Interface to HLSL Runtimes -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This provides an abstract class for HLSL code generation. Concrete // subclasses of this implement code generation for specific HLSL // runtime libraries. // //===----------------------------------------------------------------------===// #include "CGHLSLRuntime.h" #include "CGDebugInfo.h" #include "CGRecordLayout.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "HLSLBufferLayoutBuilder.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/HLSLResource.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/TargetOptions.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Frontend/HLSL/RootSignatureMetadata.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" #include #include using namespace clang; using namespace CodeGen; using namespace clang::hlsl; using namespace llvm; using llvm::hlsl::CBufferRowSizeInBytes; namespace { void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { // The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs. // Assume ValVersionStr is legal here. VersionTuple Version; if (Version.tryParse(ValVersionStr) || Version.getBuild() || Version.getSubminor() || !Version.getMinor()) { return; } uint64_t Major = Version.getMajor(); uint64_t Minor = *Version.getMinor(); auto &Ctx = M.getContext(); IRBuilder<> B(M.getContext()); MDNode *Val = MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(Major)), ConstantAsMetadata::get(B.getInt32(Minor))}); StringRef DXILValKey = "dx.valver"; auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey); DXILValMD->addOperand(Val); } void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer, ArrayRef Elements, llvm::Function *Fn, llvm::Module &M) { auto &Ctx = M.getContext(); llvm::hlsl::rootsig::MetadataBuilder RSBuilder(Ctx, Elements); MDNode *RootSignature = RSBuilder.BuildRootSignature(); ConstantAsMetadata *Version = ConstantAsMetadata::get(ConstantInt::get( llvm::Type::getInt32Ty(Ctx), llvm::to_underlying(RootSigVer))); ValueAsMetadata *EntryFunc = Fn ? ValueAsMetadata::get(Fn) : nullptr; MDNode *MDVals = MDNode::get(Ctx, {EntryFunc, RootSignature, Version}); StringRef RootSignatureValKey = "dx.rootsignatures"; auto *RootSignatureValMD = M.getOrInsertNamedMetadata(RootSignatureValKey); RootSignatureValMD->addOperand(MDVals); } // Given a MemberExpr of a resource or resource array type, find the parent // VarDecl of the struct or class instance that contains this resource and // build the full resource name based on the member access path. // // For example, for a member access like "myStructArray[0].memberA", // this function will find the VarDecl of "myStructArray" and use the // EmbeddedResourceNameBuilder to build the resource name // "myStructArray.0.memberA". static const VarDecl *findStructResourceParentDeclAndBuildName( const MemberExpr *ME, EmbeddedResourceNameBuilder &NameBuilder) { SmallVector WorkList; const VarDecl *VD = nullptr; const Expr *E = ME; for (;;) { if (const auto *DRE = dyn_cast(E)) { assert(isa(DRE->getDecl()) && "member expr base is not a var decl"); VD = cast(DRE->getDecl()); NameBuilder.pushName(VD->getName()); break; } WorkList.push_back(E); if (const auto *MExp = dyn_cast(E)) E = MExp->getBase(); else if (const auto *ICE = dyn_cast(E)) E = ICE->getSubExpr(); else if (const auto *ASE = dyn_cast(E)) E = ASE->getBase(); else if (isa(E)) // Resource member access on "this" pointer not yet implemented // (llvm/llvm-project#190299) return nullptr; else llvm_unreachable("unexpected expr type in resource member access"); assert(E && "expected valid expression"); } while (!WorkList.empty()) { E = WorkList.pop_back_val(); if (const auto *ME = dyn_cast(E)) { NameBuilder.pushName( ME->getMemberNameInfo().getName().getAsIdentifierInfo()->getName()); } else if (const auto *ICE = dyn_cast(E)) { if (ICE->getCastKind() == CK_UncheckedDerivedToBase) { CXXRecordDecl *DerivedRD = ICE->getSubExpr()->getType()->getAsCXXRecordDecl(); CXXRecordDecl *BaseRD = ICE->getType()->getAsCXXRecordDecl(); NameBuilder.pushBaseNameHierarchy(DerivedRD, BaseRD); } } else if (const auto *ASE = dyn_cast(E)) { const Expr *IdxExpr = ASE->getIdx(); std::optional Value = IdxExpr->getIntegerConstantExpr(VD->getASTContext()); assert(Value && "expected constant index in struct with resource array access"); NameBuilder.pushArrayIndex(Value->getZExtValue()); } else { llvm_unreachable("unexpected expr type in resource member access"); } } return VD; } // Given a MemberExpr of a resource or resource array type, find the // corresponding global resource declaration associated with the owning struct // or class instance via HLSLAssociatedResourceDeclAttr. static const VarDecl * findAssociatedResourceDeclForStruct(ASTContext &AST, const MemberExpr *ME) { EmbeddedResourceNameBuilder NameBuilder; const VarDecl *ParentVD = findStructResourceParentDeclAndBuildName(ME, NameBuilder); if (!ParentVD) return nullptr; if (!ParentVD->hasGlobalStorage()) return nullptr; IdentifierInfo *II = NameBuilder.getNameAsIdentifier(AST); for (const Attr *A : ParentVD->getAttrs()) { if (const auto *ADA = dyn_cast(A)) { VarDecl *AssocResVD = ADA->getResDecl(); if (AssocResVD->getIdentifier() == II) return AssocResVD; } } return nullptr; } // Find array variable declaration from DeclRef expression static const ValueDecl *getArrayDecl(ASTContext &AST, const Expr *E) { E = E->IgnoreImpCasts(); if (const auto *DRE = dyn_cast_or_null(E)) return DRE->getDecl(); if (isa(E)) return findAssociatedResourceDeclForStruct(AST, cast(E)); return nullptr; } // Find array variable declaration from nested array subscript AST nodes static const ValueDecl *getArrayDecl(ASTContext &AST, const ArraySubscriptExpr *ASE) { const Expr *E = nullptr; while (ASE != nullptr) { E = ASE->getBase()->IgnoreImpCasts(); if (!E) return nullptr; ASE = dyn_cast(E); } return getArrayDecl(AST, E); } // Get the total size of the array, or 0 if the array is unbounded. static int getTotalArraySize(ASTContext &AST, const clang::Type *Ty) { Ty = Ty->getUnqualifiedDesugaredType(); assert(Ty->isArrayType() && "expected array type"); if (Ty->isIncompleteArrayType()) return 0; return AST.getConstantArrayElementCount(cast(Ty)); } static Value *buildNameForResource(llvm::StringRef BaseName, CodeGenModule &CGM) { llvm::SmallString<64> GlobalName = {BaseName, ".str"}; return CGM.GetAddrOfConstantCString(BaseName.str(), GlobalName.c_str()) .getPointer(); } static CXXMethodDecl *lookupMethod(CXXRecordDecl *Record, StringRef Name, StorageClass SC = SC_None) { for (auto *Method : Record->methods()) { if (Method->getStorageClass() == SC && Method->getName() == Name) return Method; } return nullptr; } static CXXMethodDecl *lookupResourceInitMethodAndSetupArgs( CodeGenModule &CGM, CXXRecordDecl *ResourceDecl, llvm::Value *Range, llvm::Value *Index, StringRef Name, ResourceBindingAttrs &Binding, CallArgList &Args) { assert(Binding.hasBinding() && "at least one binding attribute expected"); ASTContext &AST = CGM.getContext(); CXXMethodDecl *CreateMethod = nullptr; Value *NameStr = buildNameForResource(Name, CGM); Value *Space = llvm::ConstantInt::get(CGM.IntTy, Binding.getSpace()); bool HasCounter = hasCounterHandle(ResourceDecl); assert((!HasCounter || Binding.hasCounterImplicitOrderID()) && "resources with counter handle must have a binding with counter " "implicit order ID"); if (Binding.isExplicit()) { // explicit binding auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, Binding.getSlot()); Args.add(RValue::get(RegSlot), AST.UnsignedIntTy); const char *Name = Binding.hasCounterImplicitOrderID() ? "__createFromBindingWithImplicitCounter" : "__createFromBinding"; CreateMethod = lookupMethod(ResourceDecl, Name, SC_Static); } else { // implicit binding auto *OrderID = llvm::ConstantInt::get(CGM.IntTy, Binding.getImplicitOrderID()); Args.add(RValue::get(OrderID), AST.UnsignedIntTy); const char *Name = Binding.hasCounterImplicitOrderID() ? "__createFromImplicitBindingWithImplicitCounter" : "__createFromImplicitBinding"; CreateMethod = lookupMethod(ResourceDecl, Name, SC_Static); } Args.add(RValue::get(Space), AST.UnsignedIntTy); Args.add(RValue::get(Range), AST.IntTy); Args.add(RValue::get(Index), AST.UnsignedIntTy); Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst())); if (HasCounter) { uint32_t CounterBinding = Binding.getCounterImplicitOrderID(); auto *CounterOrderID = llvm::ConstantInt::get(CGM.IntTy, CounterBinding); Args.add(RValue::get(CounterOrderID), AST.UnsignedIntTy); } return CreateMethod; } static void callResourceInitMethod(CodeGenFunction &CGF, CXXMethodDecl *CreateMethod, CallArgList &Args, Address ReturnAddress) { llvm::Constant *CalleeFn = CGF.CGM.GetAddrOfFunction(CreateMethod); const FunctionProtoType *Proto = CreateMethod->getType()->getAs(); const CGFunctionInfo &FnInfo = CGF.CGM.getTypes().arrangeFreeFunctionCall(Args, Proto, false); ReturnValueSlot ReturnValue(ReturnAddress, false); CGCallee Callee(CGCalleeInfo(Proto), CalleeFn); CGF.EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr); } // Initializes local resource array variable. For multi-dimensional arrays it // calls itself recursively to initialize its sub-arrays. The Index used in the // resource constructor calls will begin at StartIndex and will be incremented // for each array element. The last used resource Index is returned to the // caller. If the function returns std::nullopt, it indicates an error. static std::optional initializeLocalResourceArray( CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl, const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot, llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName, ResourceBindingAttrs &Binding, ArrayRef PrevGEPIndices, SourceLocation ArraySubsExprLoc) { ASTContext &AST = CGF.getContext(); llvm::IntegerType *IntTy = CGF.CGM.IntTy; llvm::Value *Index = StartIndex; llvm::Value *One = llvm::ConstantInt::get(IntTy, 1); const uint64_t ArraySize = ArrayTy->getSExtSize(); QualType ElemType = ArrayTy->getElementType(); Address TmpArrayAddr = ValueSlot.getAddress(); // Add additional index to the getelementptr call indices. // This index will be updated for each array element in the loops below. SmallVector GEPIndices(PrevGEPIndices); GEPIndices.push_back(llvm::ConstantInt::get(IntTy, 0)); // For array of arrays, recursively initialize the sub-arrays. if (ElemType->isArrayType()) { const ConstantArrayType *SubArrayTy = cast(ElemType); for (uint64_t I = 0; I < ArraySize; I++) { if (I > 0) { Index = CGF.Builder.CreateAdd(Index, One); GEPIndices.back() = llvm::ConstantInt::get(IntTy, I); } std::optional MaybeIndex = initializeLocalResourceArray( CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName, Binding, GEPIndices, ArraySubsExprLoc); if (!MaybeIndex) return std::nullopt; Index = *MaybeIndex; } return Index; } // For array of resources, initialize each resource in the array. llvm::Type *Ty = CGF.ConvertTypeForMem(ElemType); CharUnits ElemSize = AST.getTypeSizeInChars(ElemType); CharUnits Align = TmpArrayAddr.getAlignment().alignmentOfArrayElement(ElemSize); for (uint64_t I = 0; I < ArraySize; I++) { if (I > 0) { Index = CGF.Builder.CreateAdd(Index, One); GEPIndices.back() = llvm::ConstantInt::get(IntTy, I); } Address ReturnAddress = CGF.Builder.CreateGEP(TmpArrayAddr, GEPIndices, Ty, Align); CallArgList Args; CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs( CGF.CGM, ResourceDecl, Range, Index, ResourceName, Binding, Args); if (!CreateMethod) // This can happen if someone creates an array of structs that looks like // an HLSL resource record array but it does not have the required static // create method. No binding will be generated for it. return std::nullopt; callResourceInitMethod(CGF, CreateMethod, Args, ReturnAddress); } return Index; } } // namespace llvm::Type * CGHLSLRuntime::convertHLSLSpecificType(const Type *T, const CGHLSLOffsetInfo &OffsetInfo) { assert(T->isHLSLSpecificType() && "Not an HLSL specific type!"); // Check if the target has a specific translation for this type first. if (llvm::Type *TargetTy = CGM.getTargetCodeGenInfo().getHLSLType(CGM, T, OffsetInfo)) return TargetTy; llvm_unreachable("Generic handling of HLSL types is not supported."); } llvm::Triple::ArchType CGHLSLRuntime::getArch() { return CGM.getTarget().getTriple().getArch(); } // Emits constant global variables for buffer constants declarations // and creates metadata linking the constant globals with the buffer global. void CGHLSLRuntime::emitBufferGlobalsAndMetadata( const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV, const CGHLSLOffsetInfo &OffsetInfo) { LLVMContext &Ctx = CGM.getLLVMContext(); // get the layout struct from constant buffer target type llvm::Type *BufType = BufGV->getValueType(); llvm::StructType *LayoutStruct = cast( cast(BufType)->getTypeParameter(0)); SmallVector> DeclsWithOffset; size_t OffsetIdx = 0; for (Decl *D : BufDecl->buffer_decls()) { if (isa(D)) // Nothing to do for this declaration. continue; if (isa(D)) { // A function within an cbuffer is effectively a top-level function. CGM.EmitTopLevelDecl(D); continue; } VarDecl *VD = dyn_cast(D); if (!VD) continue; QualType VDTy = VD->getType(); if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { if (VD->getStorageClass() == SC_Static || VDTy.getAddressSpace() == LangAS::hlsl_groupshared || VDTy->isHLSLResourceRecord() || VDTy->isHLSLResourceRecordArray()) { // Emit static and groupshared variables and resource classes inside // cbuffer as regular globals CGM.EmitGlobal(VD); } continue; } DeclsWithOffset.emplace_back(VD, OffsetInfo[OffsetIdx++]); } if (!OffsetInfo.empty()) llvm::stable_sort(DeclsWithOffset, [](const auto &LHS, const auto &RHS) { return CGHLSLOffsetInfo::compareOffsets(LHS.second, RHS.second); }); // Associate the buffer global variable with its constants SmallVector BufGlobals; BufGlobals.reserve(DeclsWithOffset.size() + 1); BufGlobals.push_back(ValueAsMetadata::get(BufGV)); auto ElemIt = LayoutStruct->element_begin(); for (auto &[VD, _] : DeclsWithOffset) { if (CGM.getTargetCodeGenInfo().isHLSLPadding(*ElemIt)) ++ElemIt; assert(ElemIt != LayoutStruct->element_end() && "number of elements in layout struct does not match"); llvm::Type *LayoutType = *ElemIt++; GlobalVariable *ElemGV = cast(CGM.GetAddrOfGlobalVar(VD, LayoutType)); BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); } assert(ElemIt == LayoutStruct->element_end() && "number of elements in layout struct does not match"); // add buffer metadata to the module CGM.getModule() .getOrInsertNamedMetadata("hlsl.cbs") ->addOperand(MDNode::get(Ctx, BufGlobals)); } // Creates resource handle type for the HLSL buffer declaration static const clang::HLSLAttributedResourceType * createBufferHandleType(const HLSLBufferDecl *BufDecl) { ASTContext &AST = BufDecl->getASTContext(); QualType QT = AST.getHLSLAttributedResourceType( AST.HLSLResourceTy, AST.getCanonicalTagType(BufDecl->getLayoutStruct()), HLSLAttributedResourceType::Attributes(ResourceClass::CBuffer)); return cast(QT.getTypePtr()); } CGHLSLOffsetInfo CGHLSLOffsetInfo::fromDecl(const HLSLBufferDecl &BufDecl) { CGHLSLOffsetInfo Result; // If we don't have packoffset info, just return an empty result. if (!BufDecl.hasValidPackoffset()) return Result; for (Decl *D : BufDecl.buffer_decls()) { if (isa(D) || isa(D)) { continue; } VarDecl *VD = dyn_cast(D); if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) continue; if (!VD->hasAttrs()) { Result.Offsets.push_back(Unspecified); continue; } uint32_t Offset = Unspecified; for (auto *Attr : VD->getAttrs()) { if (auto *POA = dyn_cast(Attr)) { Offset = POA->getOffsetInBytes(); break; } auto *RBA = dyn_cast(Attr); if (RBA && RBA->getRegisterType() == HLSLResourceBindingAttr::RegisterType::C) { Offset = RBA->getSlotNumber() * CBufferRowSizeInBytes; break; } } Result.Offsets.push_back(Offset); } return Result; } // Codegen for HLSLBufferDecl void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet"); // create resource handle type for the buffer const clang::HLSLAttributedResourceType *ResHandleTy = createBufferHandleType(BufDecl); // empty constant buffer is ignored if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty()) return; // create global variable for the constant buffer CGHLSLOffsetInfo OffsetInfo = CGHLSLOffsetInfo::fromDecl(*BufDecl); llvm::Type *LayoutTy = convertHLSLSpecificType(ResHandleTy, OffsetInfo); llvm::GlobalVariable *BufGV = new GlobalVariable( LayoutTy, /*isConstant*/ false, GlobalValue::LinkageTypes::ExternalLinkage, PoisonValue::get(LayoutTy), llvm::formatv("{0}{1}", BufDecl->getName(), BufDecl->isCBuffer() ? ".cb" : ".tb"), GlobalValue::NotThreadLocal); CGM.getModule().insertGlobalVariable(BufGV); // Add globals for constant buffer elements and create metadata nodes emitBufferGlobalsAndMetadata(BufDecl, BufGV, OffsetInfo); // Initialize cbuffer from binding (implicit or explicit) initializeBufferFromBinding(BufDecl, BufGV); } void CGHLSLRuntime::addRootSignature( const HLSLRootSignatureDecl *SignatureDecl) { llvm::Module &M = CGM.getModule(); Triple T(M.getTargetTriple()); // Generated later with the function decl if not targeting root signature if (T.getEnvironment() != Triple::EnvironmentType::RootSignature) return; addRootSignatureMD(SignatureDecl->getVersion(), SignatureDecl->getRootElements(), nullptr, M); } llvm::StructType * CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { const auto Entry = LayoutTypes.find(StructType); if (Entry != LayoutTypes.end()) return Entry->getSecond(); return nullptr; } void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType, llvm::StructType *LayoutTy) { assert(getHLSLBufferLayoutType(StructType) == nullptr && "layout type for this struct already exist"); LayoutTypes[StructType] = LayoutTy; } void CGHLSLRuntime::finishCodeGen() { auto &TargetOpts = CGM.getTarget().getTargetOpts(); auto &CodeGenOpts = CGM.getCodeGenOpts(); auto &LangOpts = CGM.getLangOpts(); llvm::Module &M = CGM.getModule(); Triple T(M.getTargetTriple()); if (T.getArch() == Triple::ArchType::dxil) addDxilValVersion(TargetOpts.DxilValidatorVersion, M); if (CodeGenOpts.ResMayAlias) M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.resmayalias", 1); if (CodeGenOpts.AllResourcesBound) M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.allresourcesbound", 1); if (CodeGenOpts.OptimizationLevel == 0) M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, "dx.disable_optimizations", 1); // NativeHalfType corresponds to the -fnative-half-type clang option which is // aliased by clang-dxc's -enable-16bit-types option. This option is used to // set the UseNativeLowPrecision DXIL module flag in the DirectX backend if (LangOpts.NativeHalfType) M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.nativelowprec", 1); generateGlobalCtorDtorCalls(); } void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes( const FunctionDecl *FD, llvm::Function *Fn) { const auto *ShaderAttr = FD->getAttr(); assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr"); const StringRef ShaderAttrKindStr = "hlsl.shader"; Fn->addFnAttr(ShaderAttrKindStr, llvm::Triple::getEnvironmentTypeName(ShaderAttr->getType())); if (HLSLNumThreadsAttr *NumThreadsAttr = FD->getAttr()) { const StringRef NumThreadsKindStr = "hlsl.numthreads"; std::string NumThreadsStr = formatv("{0},{1},{2}", NumThreadsAttr->getX(), NumThreadsAttr->getY(), NumThreadsAttr->getZ()); Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr); } if (HLSLWaveSizeAttr *WaveSizeAttr = FD->getAttr()) { const StringRef WaveSizeKindStr = "hlsl.wavesize"; std::string WaveSizeStr = formatv("{0},{1},{2}", WaveSizeAttr->getMin(), WaveSizeAttr->getMax(), WaveSizeAttr->getPreferred()); Fn->addFnAttr(WaveSizeKindStr, WaveSizeStr); } // HLSL entry functions are materialized for module functions with // HLSLShaderAttr attribute. SetLLVMFunctionAttributesForDefinition called // later in the compiler-flow for such module functions is not aware of and // hence not able to set attributes of the newly materialized entry functions. // So, set attributes of entry function here, as appropriate. Fn->addFnAttr(llvm::Attribute::NoInline); if (CGM.getLangOpts().HLSLSpvEnableMaximalReconvergence) { Fn->addFnAttr("enable-maximal-reconvergence", "true"); } } static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) { if (const auto *VT = dyn_cast(Ty)) { Value *Result = PoisonValue::get(Ty); for (unsigned I = 0; I < VT->getNumElements(); ++I) { Value *Elt = B.CreateCall(F, {B.getInt32(I)}); Result = B.CreateInsertElement(Result, Elt, I); } return Result; } return B.CreateCall(F, {B.getInt32(0)}); } static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV, unsigned BuiltIn) { LLVMContext &Ctx = GV->getContext(); IRBuilder<> B(GV->getContext()); MDNode *Operands = MDNode::get( Ctx, {ConstantAsMetadata::get(B.getInt32(/* Spirv::Decoration::BuiltIn */ 11)), ConstantAsMetadata::get(B.getInt32(BuiltIn))}); MDNode *Decoration = MDNode::get(Ctx, {Operands}); GV->addMetadata("spirv.Decorations", *Decoration); } static void addLocationDecoration(llvm::GlobalVariable *GV, unsigned Location) { LLVMContext &Ctx = GV->getContext(); IRBuilder<> B(GV->getContext()); MDNode *Operands = MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(/* Location */ 30)), ConstantAsMetadata::get(B.getInt32(Location))}); MDNode *Decoration = MDNode::get(Ctx, {Operands}); GV->addMetadata("spirv.Decorations", *Decoration); } static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M, llvm::Type *Ty, const Twine &Name, unsigned BuiltInID) { auto *GV = new llvm::GlobalVariable( M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage, /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr, llvm::GlobalVariable::GeneralDynamicTLSModel, /* AddressSpace */ 7, /* isExternallyInitialized= */ true); addSPIRVBuiltinDecoration(GV, BuiltInID); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); return B.CreateLoad(Ty, GV); } static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M, llvm::Type *Ty, unsigned Location, StringRef Name) { auto *GV = new llvm::GlobalVariable( M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage, /* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr, llvm::GlobalVariable::GeneralDynamicTLSModel, /* AddressSpace */ 7, /* isExternallyInitialized= */ true); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); addLocationDecoration(GV, Location); return B.CreateLoad(Ty, GV); } llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad( llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { Twine BaseName = Twine(Semantic->getAttrName()->getName()); Twine VariableName = BaseName.concat(Twine(Index.value_or(0))); unsigned Location = SPIRVLastAssignedInputSemanticLocation; if (auto *L = Decl->getAttr()) Location = L->getLocation(); // DXC completely ignores the semantic/index pair. Location are assigned from // the first semantic to the last. llvm::ArrayType *AT = dyn_cast(Type); unsigned ElementCount = AT ? AT->getNumElements() : 1; SPIRVLastAssignedInputSemanticLocation += ElementCount; return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location, VariableName.str()); } static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M, llvm::Value *Source, unsigned Location, StringRef Name) { auto *GV = new llvm::GlobalVariable( M, Source->getType(), /* isConstant= */ false, llvm::GlobalValue::ExternalLinkage, /* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr, llvm::GlobalVariable::GeneralDynamicTLSModel, /* AddressSpace */ 8, /* isExternallyInitialized= */ false); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); addLocationDecoration(GV, Location); B.CreateStore(Source, GV); } void CGHLSLRuntime::emitSPIRVUserSemanticStore( llvm::IRBuilder<> &B, llvm::Value *Source, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { Twine BaseName = Twine(Semantic->getAttrName()->getName()); Twine VariableName = BaseName.concat(Twine(Index.value_or(0))); unsigned Location = SPIRVLastAssignedOutputSemanticLocation; if (auto *L = Decl->getAttr()) Location = L->getLocation(); // DXC completely ignores the semantic/index pair. Location are assigned from // the first semantic to the last. llvm::ArrayType *AT = dyn_cast(Source->getType()); unsigned ElementCount = AT ? AT->getNumElements() : 1; SPIRVLastAssignedOutputSemanticLocation += ElementCount; createSPIRVLocationStore(B, CGM.getModule(), Source, Location, VariableName.str()); } llvm::Value * CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { Twine BaseName = Twine(Semantic->getAttrName()->getName()); Twine VariableName = BaseName.concat(Twine(Index.value_or(0))); // DXIL packing rules etc shall be handled here. // FIXME: generate proper sigpoint, index, col, row values. // FIXME: also DXIL loads vectors element by element. SmallVector Args{B.getInt32(4), B.getInt32(0), B.getInt32(0), B.getInt8(0), llvm::PoisonValue::get(B.getInt32Ty())}; llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_load_input; llvm::Value *Value = B.CreateIntrinsic(/*ReturnType=*/Type, IntrinsicID, Args, nullptr, VariableName); return Value; } void CGHLSLRuntime::emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { // DXIL packing rules etc shall be handled here. // FIXME: generate proper sigpoint, index, col, row values. SmallVector Args{B.getInt32(4), B.getInt32(0), B.getInt32(0), B.getInt8(0), llvm::PoisonValue::get(B.getInt32Ty()), Source}; llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_store_output; B.CreateIntrinsic(/*ReturnType=*/CGM.VoidTy, IntrinsicID, Args, nullptr); } llvm::Value *CGHLSLRuntime::emitUserSemanticLoad( IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { if (CGM.getTarget().getTriple().isSPIRV()) return emitSPIRVUserSemanticLoad(B, Type, Decl, Semantic, Index); if (CGM.getTarget().getTriple().isDXIL()) return emitDXILUserSemanticLoad(B, Type, Semantic, Index); llvm_unreachable("Unsupported target for user-semantic load."); } void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { if (CGM.getTarget().getTriple().isSPIRV()) return emitSPIRVUserSemanticStore(B, Source, Decl, Semantic, Index); if (CGM.getTarget().getTriple().isDXIL()) return emitDXILUserSemanticStore(B, Source, Semantic, Index); llvm_unreachable("Unsupported target for user-semantic load."); } llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad( IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { std::string SemanticName = Semantic->getAttrName()->getName().upper(); if (SemanticName == "SV_GROUPINDEX") { llvm::Function *GroupIndex = CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic()); return B.CreateCall(FunctionCallee(GroupIndex)); } if (SemanticName == "SV_DISPATCHTHREADID") { llvm::Intrinsic::ID IntrinID = getThreadIdIntrinsic(); llvm::Function *ThreadIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); return buildVectorInput(B, ThreadIDIntrinsic, Type); } if (SemanticName == "SV_GROUPTHREADID") { llvm::Intrinsic::ID IntrinID = getGroupThreadIdIntrinsic(); llvm::Function *GroupThreadIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); return buildVectorInput(B, GroupThreadIDIntrinsic, Type); } if (SemanticName == "SV_GROUPID") { llvm::Intrinsic::ID IntrinID = getGroupIdIntrinsic(); llvm::Function *GroupIDIntrinsic = llvm::Intrinsic::isOverloaded(IntrinID) ? CGM.getIntrinsic(IntrinID, {CGM.Int32Ty}) : CGM.getIntrinsic(IntrinID); return buildVectorInput(B, GroupIDIntrinsic, Type); } const auto *ShaderAttr = FD->getAttr(); assert(ShaderAttr && "Entry point has no shader attribute"); llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); if (SemanticName == "SV_POSITION") { if (ST == Triple::EnvironmentType::Pixel) { if (CGM.getTarget().getTriple().isSPIRV()) return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, Semantic->getAttrName()->getName(), /* BuiltIn::FragCoord */ 15); if (CGM.getTarget().getTriple().isDXIL()) return emitDXILUserSemanticLoad(B, Type, Semantic, Index); } if (ST == Triple::EnvironmentType::Vertex) { return emitUserSemanticLoad(B, Type, Decl, Semantic, Index); } } if (SemanticName == "SV_VERTEXID") { if (ST == Triple::EnvironmentType::Vertex) { if (CGM.getTarget().getTriple().isSPIRV()) return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, Semantic->getAttrName()->getName(), /* BuiltIn::VertexIndex */ 42); else return emitDXILUserSemanticLoad(B, Type, Semantic, Index); } } llvm_unreachable( "Load hasn't been implemented yet for this system semantic. FIXME"); } static void createSPIRVBuiltinStore(IRBuilder<> &B, llvm::Module &M, llvm::Value *Source, const Twine &Name, unsigned BuiltInID) { auto *GV = new llvm::GlobalVariable( M, Source->getType(), /* isConstant= */ false, llvm::GlobalValue::ExternalLinkage, /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr, llvm::GlobalVariable::GeneralDynamicTLSModel, /* AddressSpace */ 8, /* isExternallyInitialized= */ false); addSPIRVBuiltinDecoration(GV, BuiltInID); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); B.CreateStore(Source, GV); } void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { std::string SemanticName = Semantic->getAttrName()->getName().upper(); if (SemanticName == "SV_POSITION") { if (CGM.getTarget().getTriple().isDXIL()) { emitDXILUserSemanticStore(B, Source, Semantic, Index); return; } if (CGM.getTarget().getTriple().isSPIRV()) { createSPIRVBuiltinStore(B, CGM.getModule(), Source, Semantic->getAttrName()->getName(), /* BuiltIn::Position */ 0); return; } } if (SemanticName == "SV_TARGET") { emitUserSemanticStore(B, Source, Decl, Semantic, Index); return; } llvm_unreachable( "Store hasn't been implemented yet for this system semantic. FIXME"); } llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad( IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) { std::optional Index = Semantic->getSemanticIndex(); if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_")) return emitSystemSemanticLoad(B, FD, Type, Decl, Semantic, Index); return emitUserSemanticLoad(B, Type, Decl, Semantic, Index); } void CGHLSLRuntime::handleScalarSemanticStore( IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) { std::optional Index = Semantic->getSemanticIndex(); if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_")) emitSystemSemanticStore(B, Source, Decl, Semantic, Index); else emitUserSemanticStore(B, Source, Decl, Semantic, Index); } std::pair> CGHLSLRuntime::handleStructSemanticLoad( IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type, const clang::DeclaratorDecl *Decl, specific_attr_iterator AttrBegin, specific_attr_iterator AttrEnd) { const llvm::StructType *ST = cast(Type); const clang::RecordDecl *RD = Decl->getType()->getAsRecordDecl(); assert(RD->getNumFields() == ST->getNumElements()); llvm::Value *Aggregate = llvm::PoisonValue::get(Type); auto FieldDecl = RD->field_begin(); for (unsigned I = 0; I < ST->getNumElements(); ++I) { auto [ChildValue, NextAttr] = handleSemanticLoad( B, FD, ST->getElementType(I), *FieldDecl, AttrBegin, AttrEnd); AttrBegin = NextAttr; assert(ChildValue); Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I); ++FieldDecl; } return std::make_pair(Aggregate, AttrBegin); } specific_attr_iterator CGHLSLRuntime::handleStructSemanticStore( IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source, const clang::DeclaratorDecl *Decl, specific_attr_iterator AttrBegin, specific_attr_iterator AttrEnd) { const llvm::StructType *ST = cast(Source->getType()); const clang::RecordDecl *RD = nullptr; if (const FunctionDecl *FD = dyn_cast(Decl)) RD = FD->getDeclaredReturnType()->getAsRecordDecl(); else RD = Decl->getType()->getAsRecordDecl(); assert(RD); assert(RD->getNumFields() == ST->getNumElements()); auto FieldDecl = RD->field_begin(); for (unsigned I = 0; I < ST->getNumElements(); ++I, ++FieldDecl) { llvm::Value *Extract = B.CreateExtractValue(Source, I); AttrBegin = handleSemanticStore(B, FD, Extract, *FieldDecl, AttrBegin, AttrEnd); } return AttrBegin; } std::pair> CGHLSLRuntime::handleSemanticLoad( IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type, const clang::DeclaratorDecl *Decl, specific_attr_iterator AttrBegin, specific_attr_iterator AttrEnd) { assert(AttrBegin != AttrEnd); if (Type->isStructTy()) return handleStructSemanticLoad(B, FD, Type, Decl, AttrBegin, AttrEnd); HLSLAppliedSemanticAttr *Attr = *AttrBegin; ++AttrBegin; return std::make_pair(handleScalarSemanticLoad(B, FD, Type, Decl, Attr), AttrBegin); } specific_attr_iterator CGHLSLRuntime::handleSemanticStore( IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source, const clang::DeclaratorDecl *Decl, specific_attr_iterator AttrBegin, specific_attr_iterator AttrEnd) { assert(AttrBegin != AttrEnd); if (Source->getType()->isStructTy()) return handleStructSemanticStore(B, FD, Source, Decl, AttrBegin, AttrEnd); HLSLAppliedSemanticAttr *Attr = *AttrBegin; ++AttrBegin; handleScalarSemanticStore(B, FD, Source, Decl, Attr); return AttrBegin; } void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn) { llvm::Module &M = CGM.getModule(); llvm::LLVMContext &Ctx = M.getContext(); auto *EntryTy = llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx), false); Function *EntryFn = Function::Create(EntryTy, Function::ExternalLinkage, FD->getName(), &M); // Copy function attributes over, we have no argument or return attributes // that can be valid on the real entry. AttributeList NewAttrs = AttributeList::get(Ctx, AttributeList::FunctionIndex, Fn->getAttributes().getFnAttrs()); EntryFn->setAttributes(NewAttrs); setHLSLEntryAttributes(FD, EntryFn); // Set the called function as internal linkage. Fn->setLinkage(GlobalValue::InternalLinkage); BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn); IRBuilder<> B(BB); llvm::SmallVector Args; SmallVector OB; if (CGM.shouldEmitConvergenceTokens()) { assert(EntryFn->isConvergent()); llvm::Value *I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, {}); llvm::Value *bundleArgs[] = {I}; OB.emplace_back("convergencectrl", bundleArgs); } SmallVector> OutputSemantic; unsigned SRetOffset = 0; for (const auto &Param : Fn->args()) { if (Param.hasStructRetAttr()) { SRetOffset = 1; llvm::Type *VarType = Param.getParamStructRetType(); llvm::Value *Var = CGM.getLangOpts().EmitLogicalPointer ? cast(B.CreateStructuredAlloca(VarType)) : cast(B.CreateAlloca(VarType)); OutputSemantic.push_back(std::make_pair(Var, VarType)); Args.push_back(Var); continue; } const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); llvm::Value *SemanticValue = nullptr; // FIXME: support inout/out parameters for semantics. if ([[maybe_unused]] HLSLParamModifierAttr *MA = PD->getAttr()) { llvm_unreachable("Not handled yet"); } else { llvm::Type *ParamType = Param.hasByValAttr() ? Param.getParamByValType() : Param.getType(); auto AttrBegin = PD->specific_attr_begin(); auto AttrEnd = PD->specific_attr_end(); auto Result = handleSemanticLoad(B, FD, ParamType, PD, AttrBegin, AttrEnd); SemanticValue = Result.first; if (!SemanticValue) return; if (Param.hasByValAttr()) { llvm::Value *Var = CGM.getLangOpts().EmitLogicalPointer ? cast( B.CreateStructuredAlloca(Param.getParamByValType())) : cast(B.CreateAlloca(Param.getParamByValType())); B.CreateStore(SemanticValue, Var); SemanticValue = Var; } } assert(SemanticValue); Args.push_back(SemanticValue); } CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB); CI->setCallingConv(Fn->getCallingConv()); if (Fn->getReturnType() != CGM.VoidTy) // Element type is unused, so set to dummy value (NULL). OutputSemantic.push_back(std::make_pair(CI, nullptr)); for (auto &SourcePair : OutputSemantic) { llvm::Value *Source = SourcePair.first; llvm::Type *ElementType = SourcePair.second; AllocaInst *AI = dyn_cast(Source); llvm::Value *SourceValue = AI ? B.CreateLoad(ElementType, Source) : Source; auto AttrBegin = FD->specific_attr_begin(); auto AttrEnd = FD->specific_attr_end(); handleSemanticStore(B, FD, SourceValue, FD, AttrBegin, AttrEnd); } B.CreateRetVoid(); // Add and identify root signature to function, if applicable for (const Attr *Attr : FD->getAttrs()) { if (const auto *RSAttr = dyn_cast(Attr)) { auto *RSDecl = RSAttr->getSignatureDecl(); addRootSignatureMD(RSDecl->getVersion(), RSDecl->getRootElements(), EntryFn, M); } } } static void gatherFunctions(SmallVectorImpl &Fns, llvm::Module &M, bool CtorOrDtor) { const auto *GV = M.getNamedGlobal(CtorOrDtor ? "llvm.global_ctors" : "llvm.global_dtors"); if (!GV) return; const auto *CA = dyn_cast(GV->getInitializer()); if (!CA) return; // The global_ctor array elements are a struct [Priority, Fn *, COMDat]. // HLSL neither supports priorities or COMDat values, so we will check those // in an assert but not handle them. for (const auto &Ctor : CA->operands()) { if (isa(Ctor)) continue; ConstantStruct *CS = cast(Ctor); assert(cast(CS->getOperand(0))->getValue() == 65535 && "HLSL doesn't support setting priority for global ctors."); assert(isa(CS->getOperand(2)) && "HLSL doesn't support COMDat for global ctors."); Fns.push_back(cast(CS->getOperand(1))); } } void CGHLSLRuntime::generateGlobalCtorDtorCalls() { llvm::Module &M = CGM.getModule(); SmallVector CtorFns; SmallVector DtorFns; gatherFunctions(CtorFns, M, true); gatherFunctions(DtorFns, M, false); // Insert a call to the global constructor at the beginning of the entry block // to externally exported functions. This is a bit of a hack, but HLSL allows // global constructors, but doesn't support driver initialization of globals. for (auto &F : M.functions()) { if (!F.hasFnAttribute("hlsl.shader")) continue; auto *Token = getConvergenceToken(F.getEntryBlock()); Instruction *IP = &*F.getEntryBlock().begin(); SmallVector OB; if (Token) { llvm::Value *bundleArgs[] = {Token}; OB.emplace_back("convergencectrl", bundleArgs); IP = Token->getNextNode(); } IRBuilder<> B(IP); for (auto *Fn : CtorFns) { auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); CI->setCallingConv(Fn->getCallingConv()); } // Insert global dtors before the terminator of the last instruction B.SetInsertPoint(F.back().getTerminator()); for (auto *Fn : DtorFns) { auto CI = B.CreateCall(FunctionCallee(Fn), {}, OB); CI->setCallingConv(Fn->getCallingConv()); } } // No need to keep global ctors/dtors for non-lib profile after call to // ctors/dtors added for entry. Triple T(M.getTargetTriple()); if (T.getEnvironment() != Triple::EnvironmentType::Library) { if (auto *GV = M.getNamedGlobal("llvm.global_ctors")) GV->eraseFromParent(); if (auto *GV = M.getNamedGlobal("llvm.global_dtors")) GV->eraseFromParent(); } } static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, Intrinsic::ID IntrID, ArrayRef Args) { LLVMContext &Ctx = CGM.getLLVMContext(); llvm::Function *InitResFunc = llvm::Function::Create( llvm::FunctionType::get(CGM.VoidTy, false), llvm::GlobalValue::InternalLinkage, ("_init_buffer_" + GV->getName()).str(), CGM.getModule()); InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline); llvm::BasicBlock *EntryBB = llvm::BasicBlock::Create(Ctx, "entry", InitResFunc); CGBuilderTy Builder(CGM, Ctx); const DataLayout &DL = CGM.getModule().getDataLayout(); Builder.SetInsertPoint(EntryBB); // Make sure the global variable is buffer resource handle llvm::Type *HandleTy = GV->getValueType(); assert(HandleTy->isTargetExtTy() && "unexpected type of the buffer global"); llvm::Value *CreateHandle = Builder.CreateIntrinsic( /*ReturnType=*/HandleTy, IntrID, Args, nullptr, Twine(GV->getName()).concat("_h")); Builder.CreateAlignedStore(CreateHandle, GV, GV->getPointerAlignment(DL)); Builder.CreateRetVoid(); CGM.AddCXXGlobalInit(InitResFunc); } void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *GV) { ResourceBindingAttrs Binding(BufDecl); assert(Binding.hasBinding() && "cbuffer/tbuffer should always have resource binding attribute"); auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0); auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1); auto *Space = llvm::ConstantInt::get(CGM.IntTy, Binding.getSpace()); Value *Name = buildNameForResource(BufDecl->getName(), CGM); // buffer with explicit binding if (Binding.isExplicit()) { llvm::Intrinsic::ID IntrinsicID = CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(); auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, Binding.getSlot()); SmallVector Args{Space, RegSlot, RangeSize, Index, Name}; initializeBuffer(CGM, GV, IntrinsicID, Args); } else { // buffer with implicit binding llvm::Intrinsic::ID IntrinsicID = CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(); auto *OrderID = llvm::ConstantInt::get(CGM.IntTy, Binding.getImplicitOrderID()); SmallVector Args{OrderID, Space, RangeSize, Index, Name}; initializeBuffer(CGM, GV, IntrinsicID, Args); } } void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *GV) { if (auto Attr = VD->getAttr()) addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn()); if (auto Attr = VD->getAttr()) addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn()); } llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { if (!CGM.shouldEmitConvergenceTokens()) return nullptr; auto E = BB.end(); for (auto I = BB.begin(); I != E; ++I) { auto *II = dyn_cast(&*I); if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) { return II; } } llvm_unreachable("Convergence token should have been emitted."); return nullptr; } class OpaqueValueVisitor : public RecursiveASTVisitor { public: llvm::SmallVector OVEs; llvm::SmallPtrSet Visited; OpaqueValueVisitor() {} bool VisitHLSLOutArgExpr(HLSLOutArgExpr *) { // These need to be bound in CodeGenFunction::EmitHLSLOutArgLValues // or CodeGenFunction::EmitHLSLOutArgExpr. If they are part of this // traversal, the temporary containing the copy out will not have // been created yet. return false; } bool VisitOpaqueValueExpr(OpaqueValueExpr *E) { // Traverse the source expression first. if (E->getSourceExpr()) TraverseStmt(E->getSourceExpr()); // Then add this OVE if we haven't seen it before. if (Visited.insert(E).second) OVEs.push_back(E); return true; } }; void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E) { typedef CodeGenFunction::OpaqueValueMappingData OpaqueValueMappingData; OpaqueValueVisitor Visitor; Visitor.TraverseStmt(E); for (auto *OVE : Visitor.OVEs) { if (CGF.isOpaqueValueEmitted(OVE)) continue; if (OpaqueValueMappingData::shouldBindAsLValue(OVE)) { LValue LV = CGF.EmitLValue(OVE->getSourceExpr()); OpaqueValueMappingData::bind(CGF, OVE, LV); } else { RValue RV = CGF.EmitAnyExpr(OVE->getSourceExpr()); OpaqueValueMappingData::bind(CGF, OVE, RV); } } } std::optional CGHLSLRuntime::emitResourceArraySubscriptExpr( const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) { assert((ArraySubsExpr->getType()->isHLSLResourceRecord() || ArraySubsExpr->getType()->isHLSLResourceRecordArray()) && "expected resource array subscript expression"); // Let clang codegen handle local and static resource array subscripts, // or when the subscript references on opaque expression (as part of // ArrayInitLoopExpr AST node). const VarDecl *ArrayDecl = dyn_cast_or_null( getArrayDecl(CGF.CGM.getContext(), ArraySubsExpr)); if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() || ArrayDecl->getStorageClass() == SC_Static) return std::nullopt; // get the resource array type ASTContext &AST = ArrayDecl->getASTContext(); const Type *ResArrayTy = ArrayDecl->getType().getTypePtr(); assert(ResArrayTy->isHLSLResourceRecordArray() && "expected array of resource classes"); // Iterate through all nested array subscript expressions to calculate // the index in the flattened resource array (if this is a multi- // dimensional array). The index is calculated as a sum of all indices // multiplied by the total size of the array at that level. Value *Index = nullptr; const ArraySubscriptExpr *ASE = ArraySubsExpr; while (ASE != nullptr) { Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx()); if (const auto *ArrayTy = dyn_cast(ASE->getType().getTypePtr())) { Value *Multiplier = llvm::ConstantInt::get( CGM.IntTy, AST.getConstantArrayElementCount(ArrayTy)); SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier); } Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex; ASE = dyn_cast(ASE->getBase()->IgnoreParenImpCasts()); } // Find binding info for the resource array. For implicit binding // an HLSLResourceBindingAttr should have been added by SemaHLSL. ResourceBindingAttrs Binding(ArrayDecl); assert(Binding.hasBinding() && "resource array must have a binding attribute"); // Find the individual resource type. QualType ResultTy = ArraySubsExpr->getType(); QualType ResourceTy = ResultTy->isArrayType() ? AST.getBaseElementType(ResultTy) : ResultTy; // Create a temporary variable for the result, which is either going // to be a single resource instance or a local array of resources (we need to // return an LValue). RawAddress TmpVar = CGF.CreateMemTemp(ResultTy); if (CGF.EmitLifetimeStart(TmpVar.getPointer())) CGF.pushFullExprCleanup( NormalEHLifetimeMarker, TmpVar); AggValueSlot ValueSlot = AggValueSlot::forAddr( TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true), AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false), AggValueSlot::DoesNotOverlap); // Calculate total array size (= range size). llvm::Value *Range = llvm::ConstantInt::getSigned( CGM.IntTy, getTotalArraySize(AST, ResArrayTy)); // If the result of the subscript operation is a single resource, call the // constructor. if (ResultTy == ResourceTy) { CallArgList Args; CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs( CGF.CGM, ResourceTy->getAsCXXRecordDecl(), Range, Index, ArrayDecl->getName(), Binding, Args); if (!CreateMethod) { // This can happen if someone creates an array of structs that looks like // an HLSL resource record array but it does not have the required static // create method. No binding will be generated for it. assert(!ResourceTy->getAsCXXRecordDecl()->isImplicit() && "create method lookup should always succeed for built-in resource " "records"); return std::nullopt; } callResourceInitMethod(CGF, CreateMethod, Args, ValueSlot.getAddress()); } else { // The result of the subscript operation is a local resource array which // needs to be initialized. const ConstantArrayType *ArrayTy = cast(ResultTy.getTypePtr()); std::optional EndIndex = initializeLocalResourceArray( CGF, ResourceTy->getAsCXXRecordDecl(), ArrayTy, ValueSlot, Range, Index, ArrayDecl->getName(), Binding, {llvm::ConstantInt::get(CGM.IntTy, 0)}, ArraySubsExpr->getExprLoc()); if (!EndIndex) return std::nullopt; } return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl); } // If RHSExpr is a global resource array, initialize all of its resources and // set them into LHS. Returns false if no copy has been performed and the // array copy should be handled by Clang codegen. bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF) { QualType ResultTy = RHSExpr->getType(); assert(ResultTy->isHLSLResourceRecordArray() && "expected resource array"); // Let Clang codegen handle local and static resource array copies. const VarDecl *ArrayDecl = dyn_cast_or_null(getArrayDecl(CGF.CGM.getContext(), RHSExpr)); if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() || ArrayDecl->getStorageClass() == SC_Static) return false; // Find binding info for the resource array. For implicit binding // the HLSLResourceBindingAttr should have been added by SemaHLSL. ResourceBindingAttrs Binding(ArrayDecl); assert(Binding.hasBinding() && "resource array must have a binding attribute"); // Find the individual resource type. ASTContext &AST = ArrayDecl->getASTContext(); QualType ResTy = AST.getBaseElementType(ResultTy); const auto *ResArrayTy = cast(ResultTy.getTypePtr()); // Use the provided LHS for the result. AggValueSlot ValueSlot = AggValueSlot::forAddr( LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true), AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false), AggValueSlot::DoesNotOverlap); // Create Value for index and total array size (= range size). int Size = getTotalArraySize(AST, ResArrayTy); llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0); llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size); // Initialize individual resources in the array into LHS. std::optional EndIndex = initializeLocalResourceArray( CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, ValueSlot, Range, Zero, ArrayDecl->getName(), Binding, {Zero}, RHSExpr->getExprLoc()); return EndIndex.has_value(); } RawAddress CGHLSLRuntime::createBufferMatrixTempAddress(const LValue &LV, SourceLocation Loc, CodeGenFunction &CGF) { assert(LV.getType()->isConstantMatrixType() && "expected matrix type"); assert(LV.getType().getAddressSpace() == LangAS::hlsl_constant && "expected cbuffer matrix"); QualType MatQualTy = LV.getType(); llvm::Type *MemTy = CGF.ConvertTypeForMem(MatQualTy); llvm::Type *LayoutTy = HLSLBufferLayoutBuilder(CGF.CGM).layOutType(MatQualTy); if (LayoutTy == MemTy) return LV.getAddress(); Address SrcAddr = LV.getAddress(); // NOTE: B\C CreateMemTemp flattens MatrixTypes which causes // overlapping GEPs in emitBufferCopy. Use CreateTempAlloca with // the non-padded layout. CharUnits Align = CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(MemTy)); RawAddress DestAlloca = CGF.CreateTempAlloca(MemTy, Align, "matrix.buf.copy"); emitBufferCopy(CGF, DestAlloca, SrcAddr, MatQualTy); return DestAlloca; } std::optional CGHLSLRuntime::emitBufferArraySubscriptExpr( const ArraySubscriptExpr *E, CodeGenFunction &CGF, llvm::function_ref EmitIdxAfterBase) { // Find the element type to index by first padding the element type per HLSL // buffer rules, and then padding out to a 16-byte register boundary if // necessary. llvm::Type *LayoutTy = HLSLBufferLayoutBuilder(CGF.CGM).layOutType(E->getType()); uint64_t LayoutSizeInBits = CGM.getDataLayout().getTypeSizeInBits(LayoutTy).getFixedValue(); CharUnits ElementSize = CharUnits::fromQuantity(LayoutSizeInBits / 8); CharUnits RowAlignedSize = ElementSize.alignTo(CharUnits::fromQuantity(16)); if (RowAlignedSize > ElementSize) { llvm::Type *Padding = CGM.getTargetCodeGenInfo().getHLSLPadding( CGM, RowAlignedSize - ElementSize); assert(Padding && "No padding type for target?"); LayoutTy = llvm::StructType::get(CGF.getLLVMContext(), {LayoutTy, Padding}, /*isPacked=*/true); } // If the layout type doesn't introduce any padding, we don't need to do // anything special. llvm::Type *OrigTy = CGF.CGM.getTypes().ConvertTypeForMem(E->getType()); if (LayoutTy == OrigTy) return std::nullopt; LValueBaseInfo EltBaseInfo; TBAAAccessInfo EltTBAAInfo; // Index into the object as-if we have an array of the padded element type, // and then dereference the element itself to avoid reading padding that may // be past the end of the in-memory object. SmallVector Indices; llvm::Value *Idx = EmitIdxAfterBase(/*Promote*/ true); Indices.push_back(Idx); Indices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0)); if (CGF.getLangOpts().EmitLogicalPointer) { // The fact that we emit an array-to-pointer decay might be an oversight, // but for now, we simply ignore it (see #179951). const CastExpr *CE = cast(E->getBase()); assert(CE->getCastKind() == CastKind::CK_ArrayToPointerDecay); LValue LV = CGF.EmitLValue(CE->getSubExpr()); Address Addr = LV.getAddress(); LayoutTy = llvm::ArrayType::get( LayoutTy, cast(Addr.getElementType())->getNumElements()); auto *GEP = cast(CGF.Builder.CreateStructuredGEP( LayoutTy, Addr.emitRawPointer(CGF), Indices, "cbufferidx")); Addr = Address(GEP, GEP->getResultElementType(), RowAlignedSize, KnownNonNull); return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } Address Addr = CGF.EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo); llvm::Value *GEP = CGF.Builder.CreateGEP(LayoutTy, Addr.emitRawPointer(CGF), Indices, "cbufferidx"); Addr = Address(GEP, Addr.getElementType(), RowAlignedSize, KnownNonNull); return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } std::optional CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF, const MemberExpr *ME) { assert((ME->getType()->isHLSLResourceRecord() || ME->getType()->isHLSLResourceRecordArray()) && "expected resource member expression"); if (ME->getType()->isHLSLResourceRecordArray()) { // FIXME: Handle member access of the whole array of resources // (llvm/llvm-project#187087). Access to individual resource array elements // is already handled in emitResourceArraySubscriptExpr. return std::nullopt; } const VarDecl *ResourceVD = findAssociatedResourceDeclForStruct(CGF.CGM.getContext(), ME); if (!ResourceVD) return std::nullopt; GlobalVariable *ResGV = cast(CGM.GetAddrOfGlobalVar(ResourceVD)); const DataLayout &DL = CGM.getDataLayout(); llvm::Type *Ty = ResGV->getValueType(); CharUnits Align = CharUnits::fromQuantity(DL.getABITypeAlign(Ty)); Address Addr = Address(ResGV, Ty, Align); LValue LV = LValue::MakeAddr(Addr, ME->getType(), CGM.getContext(), LValueBaseInfo(AlignmentSource::Type), CGM.getTBAAAccessInfo(ME->getType())); return LV; } namespace { /// Utility for emitting copies following the HLSL buffer layout rules (ie, /// copying out of a cbuffer). class HLSLBufferCopyEmitter { CodeGenFunction &CGF; Address DstPtr; Address SrcPtr; llvm::Type *LayoutTy = nullptr; SmallVector CurStoreIndices; SmallVector CurLoadIndices; // Creates & returns either a structured.gep or a ptradd/gep depending on // langopts. llvm::Value *emitAccessChain(llvm::Type *BaseTy, llvm::Value *Base, ArrayRef Indices) { bool EmitLogical = CGF.getLangOpts().EmitLogicalPointer; if (EmitLogical) return CGF.Builder.CreateAccessChain(EmitLogical, BaseTy, Base, Indices); llvm::SmallVector GEPIndices; GEPIndices.reserve(Indices.size() + 1); GEPIndices.push_back(llvm::ConstantInt::get(CGF.IntTy, 0)); GEPIndices.append(Indices.begin(), Indices.end()); return CGF.Builder.CreateAccessChain(EmitLogical, BaseTy, Base, GEPIndices); } bool isBufferLayoutArray(llvm::StructType *ST) { // A buffer layout array is a struct with two elements: the padded array, // and the last element. That is, is should look something like this: // // { [%n x { %type, %padding }], %type } // if (!ST || ST->getNumElements() != 2) return false; auto *PaddedEltsTy = dyn_cast(ST->getElementType(0)); if (!PaddedEltsTy) return false; auto *PaddedTy = dyn_cast(PaddedEltsTy->getElementType()); if (!PaddedTy || PaddedTy->getNumElements() != 2) return false; if (!CGF.CGM.getTargetCodeGenInfo().isHLSLPadding( PaddedTy->getElementType(1))) return false; llvm::Type *ElementTy = ST->getElementType(1); if (PaddedTy->getElementType(0) != ElementTy) return false; return true; } void emitBufferLayoutCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst, llvm::ArrayType *DstTy) { // Those assumptions are checked by isBufferLayoutArray. auto *SrcPaddedArrayTy = cast(SrcTy->getElementType(0)); assert(SrcPaddedArrayTy->getNumElements() + 1 == DstTy->getNumElements()); assert(cast(SrcPaddedArrayTy->getElementType()) ->getElementType(0) == SrcTy->getElementType(1)); auto *SrcDataTy = SrcTy->getElementType(1); auto Zero = llvm::ConstantInt::get(CGF.IntTy, 0); for (unsigned I = 0; I < SrcPaddedArrayTy->getNumElements(); ++I) { auto Index = llvm::ConstantInt::get(CGF.IntTy, I); auto *SrcElt = emitAccessChain(SrcTy, Src, {Zero, Index, Zero}); auto *DstElt = emitAccessChain(DstTy, Dst, {Index}); emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType()); } auto *SrcElt = emitAccessChain(SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, 1)}); auto *DstElt = emitAccessChain( DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, DstTy->getNumElements() - 1)}); emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType()); } void emitCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst, llvm::Type *DstTy) { if (isBufferLayoutArray(SrcTy)) return emitBufferLayoutCopy(Src, SrcTy, Dst, cast(DstTy)); unsigned SrcIndex = 0; unsigned DstIndex = 0; auto *DstST = cast(DstTy); while (SrcIndex < SrcTy->getNumElements() && DstIndex < DstST->getNumElements()) { if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding( SrcTy->getElementType(SrcIndex))) { SrcIndex += 1; continue; } if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding( DstST->getElementType(DstIndex))) { DstIndex += 1; continue; } auto *SrcElt = emitAccessChain( SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, SrcIndex)}); auto *DstElt = emitAccessChain( DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, DstIndex)}); emitElementCopy(SrcElt, SrcTy->getElementType(SrcIndex), DstElt, DstST->getElementType(DstIndex)); DstIndex += 1; SrcIndex += 1; } } void emitCopy(Value *Src, llvm::ArrayType *SrcTy, Value *Dst, llvm::Type *DstTy) { for (unsigned I = 0, E = SrcTy->getNumElements(); I < E; ++I) { auto *SrcElt = emitAccessChain(SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, I)}); auto *DstElt = emitAccessChain(DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, I)}); emitElementCopy(SrcElt, SrcTy->getElementType(), DstElt, cast(DstTy)->getElementType()); } } void emitElementCopy(Value *Src, llvm::Type *SrcTy, Value *Dst, llvm::Type *DstTy) { if (auto *AT = dyn_cast(SrcTy)) return emitCopy(Src, AT, Dst, DstTy); if (auto *ST = dyn_cast(SrcTy)) return emitCopy(Src, ST, Dst, DstTy); // When we have a scalar or vector element we can emit the copy. CharUnits SrcAlign = CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(SrcTy)); CharUnits DstAlign = CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(DstTy)); Address SrcAddr(Src, SrcTy, SrcAlign); Address DstAddr(Dst, DstTy, DstAlign); llvm::Value *Load = CGF.Builder.CreateLoad(SrcAddr, "cbuf.load"); CGF.Builder.CreateStore(Load, DstAddr); } public: HLSLBufferCopyEmitter(CodeGenFunction &CGF, Address DstPtr, Address SrcPtr) : CGF(CGF), DstPtr(DstPtr), SrcPtr(SrcPtr) {} bool emitCopy(QualType CType) { LayoutTy = HLSLBufferLayoutBuilder(CGF.CGM).layOutType(CType); // TODO: We should be able to fall back to a regular memcpy if the layout // type doesn't have any padding, but that runs into issues in the backend // currently. // // See https://github.com/llvm/wg-hlsl/issues/351 emitElementCopy(SrcPtr.getBasePointer(), LayoutTy, DstPtr.getBasePointer(), DstPtr.getElementType()); return true; } }; } // namespace bool CGHLSLRuntime::emitBufferCopy(CodeGenFunction &CGF, Address DstPtr, Address SrcPtr, QualType CType) { return HLSLBufferCopyEmitter(CGF, DstPtr, SrcPtr).emitCopy(CType); } LValue CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF, const MemberExpr *E) { LValue Base = CGF.EmitCheckedLValue(E->getBase(), CodeGenFunction::TCK_MemberAccess); auto *Field = dyn_cast(E->getMemberDecl()); assert(Field && "Unexpected access into HLSL buffer"); const RecordDecl *Rec = Field->getParent(); // Work out the buffer layout type to index into. QualType RecType = CGM.getContext().getCanonicalTagType(Rec); assert(RecType->isStructureOrClassType() && "Invalid type in HLSL buffer"); // Since this is a member of an object in the buffer and not the buffer's // struct/class itself, we shouldn't have any offsets on the members we need // to contend with. CGHLSLOffsetInfo EmptyOffsets; llvm::StructType *LayoutTy = HLSLBufferLayoutBuilder(CGM).layOutStruct( RecType->getAsCanonical(), EmptyOffsets); // Get the field index for the layout struct, accounting for padding. unsigned FieldIdx = CGM.getTypes().getCGRecordLayout(Rec).getLLVMFieldNo(Field); assert(FieldIdx < LayoutTy->getNumElements() && "Layout struct is smaller than member struct"); unsigned Skipped = 0; for (unsigned I = 0; I <= FieldIdx;) { llvm::Type *ElementTy = LayoutTy->getElementType(I + Skipped); if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(ElementTy)) ++Skipped; else ++I; } FieldIdx += Skipped; assert(FieldIdx < LayoutTy->getNumElements() && "Access out of bounds"); // Now index into the struct, making sure that the type we return is the // buffer layout type rather than the original type in the AST. QualType FieldType = Field->getType(); llvm::Type *FieldLLVMTy = CGM.getTypes().ConvertTypeForMem(FieldType); CharUnits Align = CharUnits::fromQuantity( CGF.CGM.getDataLayout().getABITypeAlign(FieldLLVMTy)); Value *Ptr = CGF.getLangOpts().EmitLogicalPointer ? CGF.Builder.CreateStructuredGEP( LayoutTy, Base.getPointer(CGF), llvm::ConstantInt::get(CGM.IntTy, FieldIdx)) : CGF.Builder.CreateStructGEP(LayoutTy, Base.getPointer(CGF), FieldIdx, Field->getName()); Address Addr(Ptr, FieldLLVMTy, Align, KnownNonNull); LValue LV = LValue::MakeAddr(Addr, FieldType, CGM.getContext(), LValueBaseInfo(AlignmentSource::Type), CGM.getTBAAAccessInfo(FieldType)); LV.getQuals().addCVRQualifiers(Base.getVRQualifiers()); return LV; }