/////////////////////////////////////////////////////////////////////////////// // // // DxilLinker.cpp // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // /////////////////////////////////////////////////////////////////////////////// #include "dxc/HLSL/DxilLinker.h" #include "dxc/HLSL/DxilCBuffer.h" #include "dxc/HLSL/DxilFunctionProps.h" #include "dxc/HLSL/DxilModule.h" #include "dxc/HLSL/DxilOperations.h" #include "dxc/HLSL/DxilResource.h" #include "dxc/HLSL/DxilSampler.h" #include "dxc/Support/Global.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/DenseSet.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/ADT/StringMap.h" #include #include #include "dxc/HLSL/DxilContainer.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "dxc/HLSL/DxilGenerationPass.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" using namespace llvm; using namespace hlsl; namespace { void CollectUsedFunctions(Constant *C, std::unordered_set &funcSet) { for (User *U : C->users()) { if (Instruction *I = dyn_cast(U)) { funcSet.insert(I->getParent()->getParent()); } else { Constant *CU = cast(U); CollectUsedFunctions(CU, funcSet); } } } template void AddResourceMap( const std::vector> &resTab, DXIL::ResourceClass resClass, std::unordered_map &resMap, DxilModule &DM) { for (auto &Res : resTab) { const DxilModule::ResourceLinkInfo &linkInfo = DM.GetResourceLinkInfo(resClass, Res->GetID()); resMap[linkInfo.ResRangeID] = Res.get(); } } void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap) { SmallVector Returns; // Map params. auto paramIt = NewF->arg_begin(); for (Argument ¶m : F->args()) { vmap[¶m] = (paramIt++); } llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns); // Remove params from vmap. for (Argument ¶m : F->args()) { vmap.erase(¶m); } } } // namespace namespace { struct DxilFunctionLinkInfo { DxilFunctionLinkInfo(llvm::Function *F); llvm::Function *func; std::unordered_set usedFunctions; std::unordered_set usedGVs; std::unordered_set usedResources; }; // Library to link. class DxilLib { public: DxilLib(std::unique_ptr pModule); virtual ~DxilLib() {} bool HasFunction(std::string &name); llvm::StringMap> &GetFunctionTable() { return m_functionNameMap; } bool IsInitFunc(llvm::Function *F); bool IsResourceGlobal(const llvm::Constant *GV); DxilResourceBase *GetResource(const llvm::Constant *GV); DxilModule &GetDxilModule() { return m_DM; } void LazyLoadFunction(Function *F); void BuildGlobalUsage(); void CollectUsedInitFunctions(StringSet<> &addedFunctionSet, SmallVector &workList); private: std::unique_ptr m_pModule; DxilModule &m_DM; // Map from name to Link info for extern functions. llvm::StringMap> m_functionNameMap; // Map from resource link global to resource. std::unordered_map m_resourceMap; // Set of initialize functions for global variable. std::unordered_set m_initFuncSet; }; struct DxilLinkJob; class DxilLinkerImpl : public hlsl::DxilLinker { public: DxilLinkerImpl(LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) : DxilLinker(Ctx, valMajor, valMinor) {} virtual ~DxilLinkerImpl() {} bool HasLibNameRegistered(StringRef name) override; bool RegisterLib(StringRef name, std::unique_ptr pModule, std::unique_ptr pDebugModule) override; bool AttachLib(StringRef name) override; bool DetachLib(StringRef name) override; void DetachAll() override; std::unique_ptr Link(StringRef entry, StringRef profile) override; private: bool AttachLib(DxilLib *lib); bool DetachLib(DxilLib *lib); bool AddFunctions(SmallVector &workList, DenseSet &libSet, StringSet<> &addedFunctionSet, DxilLinkJob &linkJob, bool bLazyLoadDone); // Attached libs to link. std::unordered_set m_attachedLibs; // Owner of all DxilLib. StringMap> m_LibMap; llvm::StringMap> m_functionNameMap; }; } // namespace //------------------------------------------------------------------------------ // // DxilFunctionLinkInfo methods. // DxilFunctionLinkInfo::DxilFunctionLinkInfo(Function *F) : func(F) { DXASSERT_NOMSG(F); } //------------------------------------------------------------------------------ // // DxilLib methods. // DxilLib::DxilLib(std::unique_ptr pModule) : m_pModule(std::move(pModule)), m_DM(m_pModule->GetOrCreateDxilModule()) { Module &M = *m_pModule; const std::string &MID = M.getModuleIdentifier(); // Collect function defines. for (Function &F : M.functions()) { if (F.isDeclaration()) continue; if (F.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) { // Add prefix to internal function. F.setName(MID + F.getName()); } m_functionNameMap[F.getName()] = llvm::make_unique(&F); } // Update internal global name. for (GlobalVariable &GV : M.globals()) { if (GV.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) { // Add prefix to internal global. GV.setName(MID + GV.getName()); } } } void DxilLib::LazyLoadFunction(Function *F) { DXASSERT(m_functionNameMap.count(F->getName()), "else invalid Function"); DxilFunctionLinkInfo *linkInfo = m_functionNameMap[F->getName()].get(); std::error_code EC = F->materialize(); DXASSERT_LOCALVAR(EC, !EC, "else fail to materialize"); // Build used functions for F. for (auto &BB : F->getBasicBlockList()) { for (auto &I : BB.getInstList()) { if (CallInst *CI = dyn_cast(&I)) { linkInfo->usedFunctions.insert(CI->getCalledFunction()); } } } if (m_DM.HasDxilFunctionProps(F)) { DxilFunctionProps &props = m_DM.GetDxilFunctionProps(F); if (props.IsHS()) { // Add patch constant function to usedFunctions of entry. Function *patchConstantFunc = props.ShaderProps.HS.patchConstantFunc; linkInfo->usedFunctions.insert(patchConstantFunc); } } // Used globals will be build before link. } void DxilLib::BuildGlobalUsage() { Module &M = *m_pModule; // Collect init functions for static globals. if (GlobalVariable *Ctors = M.getGlobalVariable("llvm.global_ctors")) { if (ConstantArray *CA = dyn_cast(Ctors->getInitializer())) { for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) { if (isa(*i)) continue; ConstantStruct *CS = cast(*i); if (isa(CS->getOperand(1))) continue; // Must have a function or null ptr. if (!isa(CS->getOperand(1))) continue; Function *Ctor = cast(CS->getOperand(1)); assert(Ctor->getReturnType()->isVoidTy() && Ctor->arg_size() == 0 && "function type must be void (void)"); // Add Ctor. m_initFuncSet.insert(Ctor); LazyLoadFunction(Ctor); } } } // Build used globals. for (GlobalVariable &GV : M.globals()) { std::unordered_set funcSet; CollectUsedFunctions(&GV, funcSet); for (Function *F : funcSet) { DXASSERT(m_functionNameMap.count(F->getName()), "must exist in table"); DxilFunctionLinkInfo *linkInfo = m_functionNameMap[F->getName()].get(); linkInfo->usedGVs.insert(&GV); } } // Build resource map. AddResourceMap(m_DM.GetUAVs(), DXIL::ResourceClass::UAV, m_resourceMap, m_DM); AddResourceMap(m_DM.GetSRVs(), DXIL::ResourceClass::SRV, m_resourceMap, m_DM); AddResourceMap(m_DM.GetCBuffers(), DXIL::ResourceClass::CBuffer, m_resourceMap, m_DM); AddResourceMap(m_DM.GetSamplers(), DXIL::ResourceClass::Sampler, m_resourceMap, m_DM); } void DxilLib::CollectUsedInitFunctions(StringSet<> &addedFunctionSet, SmallVector &workList) { // Add init functions to used functions. for (Function *Ctor : m_initFuncSet) { DXASSERT(m_functionNameMap.count(Ctor->getName()), "must exist in internal table"); DxilFunctionLinkInfo *linkInfo = m_functionNameMap[Ctor->getName()].get(); // If function other than Ctor used GV of Ctor. // Add Ctor to usedFunctions for it. for (GlobalVariable *GV : linkInfo->usedGVs) { std::unordered_set funcSet; CollectUsedFunctions(GV, funcSet); bool bAdded = false; for (Function *F : funcSet) { if (F == Ctor) continue; // If F is added for link, add init func to workList. if (addedFunctionSet.count(F->getName())) { workList.emplace_back(Ctor->getName()); bAdded = true; break; } } if (bAdded) break; } } } bool DxilLib::HasFunction(std::string &name) { return m_functionNameMap.count(name); } bool DxilLib::IsInitFunc(llvm::Function *F) { return m_initFuncSet.count(F); } bool DxilLib::IsResourceGlobal(const llvm::Constant *GV) { return m_resourceMap.count(GV); } DxilResourceBase *DxilLib::GetResource(const llvm::Constant *GV) { if (IsResourceGlobal(GV)) return m_resourceMap[GV]; else return nullptr; } namespace { // Create module from link defines. struct DxilLinkJob { DxilLinkJob(LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) : m_ctx(Ctx), m_valMajor(valMajor), m_valMinor(valMinor) {} std::unique_ptr Link(std::pair &entryLinkPair, StringRef profile); void RunPreparePass(llvm::Module &M); void AddFunction(std::pair &linkPair); void AddFunction(llvm::Function *F); private: bool AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV); void AddResourceToDM(DxilModule &DM); std::unordered_map m_functionDefs; llvm::StringMap m_dxilFunctions; // New created functions. llvm::StringMap m_newFunctions; // New created globals. llvm::StringMap m_newGlobals; // Map for resource. llvm::StringMap> m_resourceMap; LLVMContext &m_ctx; unsigned m_valMajor, m_valMinor; }; } // namespace namespace { const char kUndefFunction[] = "Cannot find definition of function "; const char kRedefineFunction[] = "Definition already exists for function "; const char kRedefineGlobal[] = "Definition already exists for global variable "; const char kInvalidProfile[] = " is invalid profile to link"; const char kShaderKindMismatch[] = "Profile mismatch between entry function and target profile:"; const char kNoEntryProps[] = "Cannot find function property for entry function "; const char kRedefineResource[] = "Resource already exists as "; const char kInvalidValidatorVersion[] = "Validator version does not support target profile "; } // namespace //------------------------------------------------------------------------------ // // DxilLinkJob methods. // namespace { // Helper function to check type match. bool IsMatchedType(Type *Ty0, Type *Ty); StringRef RemoveNameSuffix(StringRef Name) { size_t DotPos = Name.rfind('.'); if (DotPos != StringRef::npos && Name.back() != '.' && isdigit(static_cast(Name[DotPos + 1]))) Name = Name.substr(0, DotPos); return Name; } bool IsMatchedStructType(StructType *ST0, StructType *ST) { StringRef Name0 = RemoveNameSuffix(ST0->getName()); StringRef Name = RemoveNameSuffix(ST->getName()); if (Name0 != Name) return false; if (ST0->getNumElements() != ST->getNumElements()) return false; if (ST0->isLayoutIdentical(ST)) return true; for (unsigned i = 0; i < ST->getNumElements(); i++) { Type *Ty = ST->getElementType(i); Type *Ty0 = ST0->getElementType(i); if (!IsMatchedType(Ty, Ty0)) return false; } return true; } bool IsMatchedArrayType(ArrayType *AT0, ArrayType *AT) { if (AT0->getNumElements() != AT->getNumElements()) return false; return IsMatchedType(AT0->getElementType(), AT->getElementType()); } bool IsMatchedType(Type *Ty0, Type *Ty) { if (Ty0->isStructTy() && Ty->isStructTy()) { StructType *ST0 = cast(Ty0); StructType *ST = cast(Ty); return IsMatchedStructType(ST0, ST); } if (Ty0->isArrayTy() && Ty->isArrayTy()) { ArrayType *AT0 = cast(Ty0); ArrayType *AT = cast(Ty); return IsMatchedArrayType(AT0, AT); } if (Ty0->isPointerTy() && Ty->isPointerTy()) { if (Ty0->getPointerAddressSpace() != Ty->getPointerAddressSpace()) return false; return IsMatchedType(Ty0->getPointerElementType(), Ty->getPointerElementType()); } return Ty0 == Ty; } } // namespace bool DxilLinkJob::AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV) { if (m_resourceMap.count(res->GetGlobalName())) { DxilResourceBase *res0 = m_resourceMap[res->GetGlobalName()].first; Type *Ty0 = res0->GetGlobalSymbol()->getType()->getPointerElementType(); Type *Ty = res->GetGlobalSymbol()->getType()->getPointerElementType(); // Make sure res0 match res. bool bMatch = IsMatchedType(Ty0, Ty); if (!bMatch) { // Report error. m_ctx.emitError(Twine(kRedefineResource) + res->GetResClassName() + " for " + res->GetGlobalName()); return false; } } else { m_resourceMap[res->GetGlobalName()] = std::make_pair(res, GV); } return true; } void DxilLinkJob::AddResourceToDM(DxilModule &DM) { for (auto &it : m_resourceMap) { DxilResourceBase *res = it.second.first; GlobalVariable *GV = it.second.second; unsigned ID = 0; DxilResourceBase *basePtr = nullptr; switch (res->GetClass()) { case DXIL::ResourceClass::UAV: { std::unique_ptr pUAV = llvm::make_unique(); DxilResource *ptr = pUAV.get(); // Copy the content. *ptr = *(static_cast(res)); ID = DM.AddUAV(std::move(pUAV)); basePtr = &DM.GetUAV(ID); } break; case DXIL::ResourceClass::SRV: { std::unique_ptr pSRV = llvm::make_unique(); DxilResource *ptr = pSRV.get(); // Copy the content. *ptr = *(static_cast(res)); ID = DM.AddSRV(std::move(pSRV)); basePtr = &DM.GetSRV(ID); } break; case DXIL::ResourceClass::CBuffer: { std::unique_ptr pCBuf = llvm::make_unique(); DxilCBuffer *ptr = pCBuf.get(); // Copy the content. *ptr = *(static_cast(res)); ID = DM.AddCBuffer(std::move(pCBuf)); basePtr = &DM.GetCBuffer(ID); } break; case DXIL::ResourceClass::Sampler: { std::unique_ptr pSampler = llvm::make_unique(); DxilSampler *ptr = pSampler.get(); // Copy the content. *ptr = *(static_cast(res)); ID = DM.AddSampler(std::move(pSampler)); basePtr = &DM.GetSampler(ID); } default: DXASSERT(res->GetClass() == DXIL::ResourceClass::Sampler, "else invalid resource"); break; } // Update ID. basePtr->SetID(ID); Constant *rangeID = ConstantInt::get(GV->getType()->getElementType(), ID); for (User *U : GV->users()) { LoadInst *LI = cast(U); LI->replaceAllUsesWith(rangeID); } } } std::unique_ptr DxilLinkJob::Link(std::pair &entryLinkPair, StringRef profile) { Function *entryFunc = entryLinkPair.first->func; DxilModule &entryDM = entryLinkPair.second->GetDxilModule(); if (!entryDM.HasDxilFunctionProps(entryFunc)) { // Cannot get function props. m_ctx.emitError(Twine(kNoEntryProps) + entryFunc->getName()); return nullptr; } DxilFunctionProps props = entryDM.GetDxilFunctionProps(entryFunc); if (props.shaderKind == DXIL::ShaderKind::Library || props.shaderKind == DXIL::ShaderKind::Invalid || (props.shaderKind >= DXIL::ShaderKind::RayGeneration && props.shaderKind <= DXIL::ShaderKind::Callable)) { m_ctx.emitError(profile + Twine(kInvalidProfile)); // Invalid profile. return nullptr; } const ShaderModel *pSM = ShaderModel::GetByName(profile.data()); if (pSM->GetKind() != props.shaderKind) { // Shader kind mismatch. m_ctx.emitError(Twine(kShaderKindMismatch) + profile + " and " + ShaderModel::GetKindName(props.shaderKind)); return nullptr; } // Create new module. std::unique_ptr pM = llvm::make_unique(entryFunc->getName(), entryDM.GetCtx()); // Set target. pM->setTargetTriple(entryDM.GetModule()->getTargetTriple()); // Add dxil operation functions before create DxilModule. for (auto &it : m_dxilFunctions) { Function *F = it.second; Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(), F->getName(), pM.get()); NewF->setAttributes(F->getAttributes()); m_newFunctions[NewF->getName()] = NewF; } // Create DxilModule. const bool bSkipInit = true; DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit); DM.SetShaderModel(pSM); // Set Validator version, verifying that it supports the requested profile unsigned minValMajor, minValMinor; DM.GetMinValidatorVersion(minValMajor, minValMinor); if (minValMajor > m_valMajor || (minValMajor == m_valMajor && minValMinor > m_valMinor)) { m_ctx.emitError(Twine(kInvalidValidatorVersion) + profile); return nullptr; } DM.SetValidatorVersion(m_valMajor, m_valMinor); // Add type sys DxilTypeSystem &typeSys = DM.GetTypeSystem(); ValueToValueMapTy vmap; std::unordered_set initFuncSet; // Add function for (auto &it : m_functionDefs) { DxilFunctionLinkInfo *linkInfo = it.first; DxilLib *pLib = it.second; DxilModule &tmpDM = pLib->GetDxilModule(); DxilTypeSystem &tmpTypeSys = tmpDM.GetTypeSystem(); Function *F = linkInfo->func; Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(), F->getName(), pM.get()); NewF->setAttributes(F->getAttributes()); if (!NewF->hasFnAttribute(llvm::Attribute::NoInline)) NewF->addFnAttr(llvm::Attribute::AlwaysInline); if (DxilFunctionAnnotation *funcAnnotation = tmpTypeSys.GetFunctionAnnotation(F)) { // Clone funcAnnotation to typeSys. typeSys.CopyFunctionAnnotation(NewF, F, tmpTypeSys); } // Add to function map. m_newFunctions[NewF->getName()] = NewF; if (pLib->IsInitFunc(F)) initFuncSet.insert(NewF); vmap[F] = NewF; } // Set Entry Function *NewEntryFunc = m_newFunctions[entryFunc->getName()]; DM.SetEntryFunction(NewEntryFunc); DM.SetEntryFunctionName(entryFunc->getName()); if (entryDM.HasDxilEntrySignature(entryFunc)) { // Add signature. DxilEntrySignature &entrySig = entryDM.GetDxilEntrySignature(entryFunc); std::unique_ptr newSig = std::make_unique(entrySig); DM.ResetEntrySignature(newSig.release()); } if (NewEntryFunc->hasFnAttribute(llvm::Attribute::AlwaysInline)) NewEntryFunc->removeFnAttr(llvm::Attribute::AlwaysInline); if (props.IsHS()) { Function *patchConstantFunc = props.ShaderProps.HS.patchConstantFunc; Function *newPatchConstantFunc = m_newFunctions[patchConstantFunc->getName()]; props.ShaderProps.HS.patchConstantFunc = newPatchConstantFunc; if (newPatchConstantFunc->hasFnAttribute(llvm::Attribute::AlwaysInline)) newPatchConstantFunc->removeFnAttr(llvm::Attribute::AlwaysInline); } // Set EntryProps DM.SetShaderProperties(&props); // Debug info. // Add global bool bSuccess = true; for (auto &it : m_functionDefs) { DxilFunctionLinkInfo *linkInfo = it.first; DxilLib *pLib = it.second; for (GlobalVariable *GV : linkInfo->usedGVs) { // Skip added globals. if (m_newGlobals.count(GV->getName())) { if (vmap.find(GV) == vmap.end()) { if (DxilResourceBase *res = pLib->GetResource(GV)) { // For resource of same name, if class and type match, just map to // same NewGV. GlobalVariable *NewGV = m_newGlobals[GV->getName()]; if (AddResource(res, NewGV)) { vmap[GV] = NewGV; } else { bSuccess = false; } continue; } // Redefine of global. m_ctx.emitError(Twine(kRedefineGlobal) + GV->getName()); bSuccess = false; } continue; } Constant *Initializer = nullptr; if (GV->hasInitializer()) Initializer = GV->getInitializer(); GlobalVariable *NewGV = new GlobalVariable( *pM, GV->getType()->getElementType(), GV->isConstant(), GV->getLinkage(), Initializer, GV->getName(), /*InsertBefore*/ nullptr, GV->getThreadLocalMode(), GV->getType()->getAddressSpace(), GV->isExternallyInitialized()); m_newGlobals[GV->getName()] = NewGV; vmap[GV] = NewGV; if (DxilResourceBase *res = pLib->GetResource(GV)) { bSuccess &= AddResource(res, NewGV); } } } if (!bSuccess) return nullptr; // Clone functions. for (auto &it : m_functionDefs) { DxilFunctionLinkInfo *linkInfo = it.first; Function *F = linkInfo->func; Function *NewF = m_newFunctions[F->getName()]; // Add dxil functions to vmap. for (Function *UsedF : linkInfo->usedFunctions) { if (!vmap.count(UsedF)) { // Extern function need match by name DXASSERT(m_newFunctions.count(UsedF->getName()), "Must have new function."); vmap[UsedF] = m_newFunctions[UsedF->getName()]; } } CloneFunction(F, NewF, vmap); } // Call global constrctor. IRBuilder<> Builder( DM.GetEntryFunction()->getEntryBlock().getFirstInsertionPt()); for (auto &it : m_functionDefs) { DxilFunctionLinkInfo *linkInfo = it.first; DxilLib *pLib = it.second; Function *F = linkInfo->func; if (pLib->IsInitFunc(F)) { Function *NewF = m_newFunctions[F->getName()]; Builder.CreateCall(NewF); } } // Refresh intrinsic cache. DM.GetOP()->RefreshCache(); // Add resource to DM. // This should be after functions cloned. AddResourceToDM(DM); RunPreparePass(*pM); return pM; } void DxilLinkJob::AddFunction( std::pair &linkPair) { m_functionDefs[linkPair.first] = linkPair.second; } void DxilLinkJob::AddFunction(llvm::Function *F) { m_dxilFunctions[F->getName()] = F; } void DxilLinkJob::RunPreparePass(Module &M) { legacy::PassManager PM; PM.add(createAlwaysInlinerPass(/*InsertLifeTime*/ false)); PM.add(createDxilDeadFunctionEliminationPass()); // mem2reg. PM.add(createPromoteMemoryToRegisterPass()); // Remove unused functions. PM.add(createDeadCodeEliminationPass()); PM.add(createGlobalDCEPass()); PM.add(createSimplifyInstPass()); PM.add(createCFGSimplificationPass()); PM.add(createDxilCondenseResourcesPass()); PM.add(createDxilFinalizeModulePass()); PM.add(createComputeViewIdStatePass()); PM.add(createDxilDeadFunctionEliminationPass()); PM.add(createNoPausePassesPass()); PM.add(createDxilEmitMetadataPass()); PM.run(M); } //------------------------------------------------------------------------------ // // DxilLinkerImpl methods. // bool DxilLinkerImpl::HasLibNameRegistered(StringRef name) { return m_LibMap.count(name); } bool DxilLinkerImpl::RegisterLib(StringRef name, std::unique_ptr pModule, std::unique_ptr pDebugModule) { if (m_LibMap.count(name)) return false; std::unique_ptr pM = pDebugModule ? std::move(pDebugModule) : std::move(pModule); if (!pM) return false; pM->setModuleIdentifier(name); std::unique_ptr pLib = std::make_unique(std::move(pM)); m_LibMap[name] = std::move(pLib); return true; } bool DxilLinkerImpl::AttachLib(StringRef name) { auto iter = m_LibMap.find(name); if (iter == m_LibMap.end()) { return false; } return AttachLib(iter->second.get()); } bool DxilLinkerImpl::DetachLib(StringRef name) { auto iter = m_LibMap.find(name); if (iter == m_LibMap.end()) { return false; } return DetachLib(iter->second.get()); } void DxilLinkerImpl::DetachAll() { m_functionNameMap.clear(); m_attachedLibs.clear(); } bool DxilLinkerImpl::AttachLib(DxilLib *lib) { if (!lib) { // Invalid arg. return false; } if (m_attachedLibs.count(lib)) return false; StringMap> &funcTable = lib->GetFunctionTable(); bool bSuccess = true; for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) { StringRef name = it->getKey(); if (m_functionNameMap.count(name)) { // Redefine of function. m_ctx.emitError(Twine(kRedefineFunction) + name); bSuccess = false; continue; } m_functionNameMap[name] = std::make_pair(it->second.get(), lib); } if (bSuccess) { m_attachedLibs.insert(lib); } else { for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) { StringRef name = it->getKey(); auto iter = m_functionNameMap.find(name); if (iter == m_functionNameMap.end()) continue; // Remove functions of lib. if (m_functionNameMap[name].second == lib) m_functionNameMap.erase(name); } } return bSuccess; } bool DxilLinkerImpl::DetachLib(DxilLib *lib) { if (!lib) { // Invalid arg. return false; } if (!m_attachedLibs.count(lib)) return false; m_attachedLibs.erase(lib); // Remove functions from lib. StringMap> &funcTable = lib->GetFunctionTable(); for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) { StringRef name = it->getKey(); m_functionNameMap.erase(name); } return true; } bool DxilLinkerImpl::AddFunctions(SmallVector &workList, DenseSet &libSet, StringSet<> &addedFunctionSet, DxilLinkJob &linkJob, bool bLazyLoadDone) { while (!workList.empty()) { StringRef name = workList.pop_back_val(); // Ignore added function. if (addedFunctionSet.count(name)) continue; if (!m_functionNameMap.count(name)) { // Cannot find function, report error. m_ctx.emitError(Twine(kUndefFunction) + name); return false; } std::pair &linkPair = m_functionNameMap[name]; linkJob.AddFunction(linkPair); DxilLib *pLib = linkPair.second; libSet.insert(pLib); if (!bLazyLoadDone) { Function *F = linkPair.first->func; pLib->LazyLoadFunction(F); } for (Function *F : linkPair.first->usedFunctions) { if (hlsl::OP::IsDxilOpFunc(F)) { // Add dxil operations directly. linkJob.AddFunction(F); } else { // Push function name to work list. workList.emplace_back(F->getName()); } } addedFunctionSet.insert(name); } return true; } std::unique_ptr DxilLinkerImpl::Link(StringRef entry, StringRef profile) { StringSet<> addedFunctionSet; SmallVector workList; workList.emplace_back(entry); DxilLinkJob linkJob(m_ctx, m_valMajor, m_valMinor); DenseSet libSet; if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob, /*bLazyLoadDone*/ false)) return nullptr; // Save global users. for (auto &pLib : libSet) { pLib->BuildGlobalUsage(); } // Save global ctor users. for (auto &pLib : libSet) { pLib->CollectUsedInitFunctions(addedFunctionSet, workList); } // Add init functions if used. // All init function already loaded in BuildGlobalUsage, so set bLazyLoad // false here. if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob, /*bLazyLoadDone*/ true)) return nullptr; std::pair &entryLinkPair = m_functionNameMap[entry]; return linkJob.Link(entryLinkPair, profile); } namespace hlsl { DxilLinker *DxilLinker::CreateLinker(LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) { return new DxilLinkerImpl(Ctx, valMajor, valMinor); } } // namespace hlsl