| //===- lib/Linker/LinkModules.cpp - Module Linker Implementation ----------===// | |
| // | |
| // 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 file implements the LLVM module linker. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "LinkDiagnosticInfo.h" | |
| #include "llvm-c/Linker.h" | |
| #include "llvm/ADT/SetVector.h" | |
| #include "llvm/IR/Comdat.h" | |
| #include "llvm/IR/DiagnosticPrinter.h" | |
| #include "llvm/IR/GlobalValue.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Linker/Linker.h" | |
| #include "llvm/Support/Error.h" | |
| using namespace llvm; | |
| namespace { | |
| /// This is an implementation class for the LinkModules function, which is the | |
| /// entrypoint for this file. | |
| class ModuleLinker { | |
| IRMover &Mover; | |
| std::unique_ptr<Module> SrcM; | |
| SetVector<GlobalValue *> ValuesToLink; | |
| /// For symbol clashes, prefer those from Src. | |
| unsigned Flags; | |
| /// List of global value names that should be internalized. | |
| StringSet<> Internalize; | |
| /// Function that will perform the actual internalization. The reason for a | |
| /// callback is that the linker cannot call internalizeModule without | |
| /// creating a circular dependency between IPO and the linker. | |
| std::function<void(Module &, const StringSet<> &)> InternalizeCallback; | |
| /// Used as the callback for lazy linking. | |
| /// The mover has just hit GV and we have to decide if it, and other members | |
| /// of the same comdat, should be linked. Every member to be linked is passed | |
| /// to Add. | |
| void addLazyFor(GlobalValue &GV, const IRMover::ValueAdder &Add); | |
| bool shouldOverrideFromSrc() { return Flags & Linker::OverrideFromSrc; } | |
| bool shouldLinkOnlyNeeded() { return Flags & Linker::LinkOnlyNeeded; } | |
| bool shouldLinkFromSource(bool &LinkFromSrc, const GlobalValue &Dest, | |
| const GlobalValue &Src); | |
| /// Should we have mover and linker error diag info? | |
| bool emitError(const Twine &Message) { | |
| SrcM->getContext().diagnose(LinkDiagnosticInfo(DS_Error, Message)); | |
| return true; | |
| } | |
| bool getComdatLeader(Module &M, StringRef ComdatName, | |
| const GlobalVariable *&GVar); | |
| bool computeResultingSelectionKind(StringRef ComdatName, | |
| Comdat::SelectionKind Src, | |
| Comdat::SelectionKind Dst, | |
| Comdat::SelectionKind &Result, | |
| bool &LinkFromSrc); | |
| std::map<const Comdat *, std::pair<Comdat::SelectionKind, bool>> | |
| ComdatsChosen; | |
| bool getComdatResult(const Comdat *SrcC, Comdat::SelectionKind &SK, | |
| bool &LinkFromSrc); | |
| // Keep track of the lazy linked global members of each comdat in source. | |
| DenseMap<const Comdat *, std::vector<GlobalValue *>> LazyComdatMembers; | |
| /// Given a global in the source module, return the global in the | |
| /// destination module that is being linked to, if any. | |
| GlobalValue *getLinkedToGlobal(const GlobalValue *SrcGV) { | |
| Module &DstM = Mover.getModule(); | |
| // If the source has no name it can't link. If it has local linkage, | |
| // there is no name match-up going on. | |
| if (!SrcGV->hasName() || GlobalValue::isLocalLinkage(SrcGV->getLinkage())) | |
| return nullptr; | |
| // Otherwise see if we have a match in the destination module's symtab. | |
| GlobalValue *DGV = DstM.getNamedValue(SrcGV->getName()); | |
| if (!DGV) | |
| return nullptr; | |
| // If we found a global with the same name in the dest module, but it has | |
| // internal linkage, we are really not doing any linkage here. | |
| if (DGV->hasLocalLinkage()) | |
| return nullptr; | |
| // Otherwise, we do in fact link to the destination global. | |
| return DGV; | |
| } | |
| /// Drop GV if it is a member of a comdat that we are dropping. | |
| /// This can happen with COFF's largest selection kind. | |
| void dropReplacedComdat(GlobalValue &GV, | |
| const DenseSet<const Comdat *> &ReplacedDstComdats); | |
| bool linkIfNeeded(GlobalValue &GV); | |
| public: | |
| ModuleLinker(IRMover &Mover, std::unique_ptr<Module> SrcM, unsigned Flags, | |
| std::function<void(Module &, const StringSet<> &)> | |
| InternalizeCallback = {}) | |
| : Mover(Mover), SrcM(std::move(SrcM)), Flags(Flags), | |
| InternalizeCallback(std::move(InternalizeCallback)) {} | |
| bool run(); | |
| }; | |
| } | |
| static GlobalValue::VisibilityTypes | |
| getMinVisibility(GlobalValue::VisibilityTypes A, | |
| GlobalValue::VisibilityTypes B) { | |
| if (A == GlobalValue::HiddenVisibility || B == GlobalValue::HiddenVisibility) | |
| return GlobalValue::HiddenVisibility; | |
| if (A == GlobalValue::ProtectedVisibility || | |
| B == GlobalValue::ProtectedVisibility) | |
| return GlobalValue::ProtectedVisibility; | |
| return GlobalValue::DefaultVisibility; | |
| } | |
| bool ModuleLinker::getComdatLeader(Module &M, StringRef ComdatName, | |
| const GlobalVariable *&GVar) { | |
| const GlobalValue *GVal = M.getNamedValue(ComdatName); | |
| if (const auto *GA = dyn_cast_or_null<GlobalAlias>(GVal)) { | |
| GVal = GA->getBaseObject(); | |
| if (!GVal) | |
| // We cannot resolve the size of the aliasee yet. | |
| return emitError("Linking COMDATs named '" + ComdatName + | |
| "': COMDAT key involves incomputable alias size."); | |
| } | |
| GVar = dyn_cast_or_null<GlobalVariable>(GVal); | |
| if (!GVar) | |
| return emitError( | |
| "Linking COMDATs named '" + ComdatName + | |
| "': GlobalVariable required for data dependent selection!"); | |
| return false; | |
| } | |
| bool ModuleLinker::computeResultingSelectionKind(StringRef ComdatName, | |
| Comdat::SelectionKind Src, | |
| Comdat::SelectionKind Dst, | |
| Comdat::SelectionKind &Result, | |
| bool &LinkFromSrc) { | |
| Module &DstM = Mover.getModule(); | |
| // The ability to mix Comdat::SelectionKind::Any with | |
| // Comdat::SelectionKind::Largest is a behavior that comes from COFF. | |
| bool DstAnyOrLargest = Dst == Comdat::SelectionKind::Any || | |
| Dst == Comdat::SelectionKind::Largest; | |
| bool SrcAnyOrLargest = Src == Comdat::SelectionKind::Any || | |
| Src == Comdat::SelectionKind::Largest; | |
| if (DstAnyOrLargest && SrcAnyOrLargest) { | |
| if (Dst == Comdat::SelectionKind::Largest || | |
| Src == Comdat::SelectionKind::Largest) | |
| Result = Comdat::SelectionKind::Largest; | |
| else | |
| Result = Comdat::SelectionKind::Any; | |
| } else if (Src == Dst) { | |
| Result = Dst; | |
| } else { | |
| return emitError("Linking COMDATs named '" + ComdatName + | |
| "': invalid selection kinds!"); | |
| } | |
| switch (Result) { | |
| case Comdat::SelectionKind::Any: | |
| // Go with Dst. | |
| LinkFromSrc = false; | |
| break; | |
| case Comdat::SelectionKind::NoDuplicates: | |
| return emitError("Linking COMDATs named '" + ComdatName + | |
| "': noduplicates has been violated!"); | |
| case Comdat::SelectionKind::ExactMatch: | |
| case Comdat::SelectionKind::Largest: | |
| case Comdat::SelectionKind::SameSize: { | |
| const GlobalVariable *DstGV; | |
| const GlobalVariable *SrcGV; | |
| if (getComdatLeader(DstM, ComdatName, DstGV) || | |
| getComdatLeader(*SrcM, ComdatName, SrcGV)) | |
| return true; | |
| const DataLayout &DstDL = DstM.getDataLayout(); | |
| const DataLayout &SrcDL = SrcM->getDataLayout(); | |
| uint64_t DstSize = DstDL.getTypeAllocSize(DstGV->getValueType()); | |
| uint64_t SrcSize = SrcDL.getTypeAllocSize(SrcGV->getValueType()); | |
| if (Result == Comdat::SelectionKind::ExactMatch) { | |
| if (SrcGV->getInitializer() != DstGV->getInitializer()) | |
| return emitError("Linking COMDATs named '" + ComdatName + | |
| "': ExactMatch violated!"); | |
| LinkFromSrc = false; | |
| } else if (Result == Comdat::SelectionKind::Largest) { | |
| LinkFromSrc = SrcSize > DstSize; | |
| } else if (Result == Comdat::SelectionKind::SameSize) { | |
| if (SrcSize != DstSize) | |
| return emitError("Linking COMDATs named '" + ComdatName + | |
| "': SameSize violated!"); | |
| LinkFromSrc = false; | |
| } else { | |
| llvm_unreachable("unknown selection kind"); | |
| } | |
| break; | |
| } | |
| } | |
| return false; | |
| } | |
| bool ModuleLinker::getComdatResult(const Comdat *SrcC, | |
| Comdat::SelectionKind &Result, | |
| bool &LinkFromSrc) { | |
| Module &DstM = Mover.getModule(); | |
| Comdat::SelectionKind SSK = SrcC->getSelectionKind(); | |
| StringRef ComdatName = SrcC->getName(); | |
| Module::ComdatSymTabType &ComdatSymTab = DstM.getComdatSymbolTable(); | |
| Module::ComdatSymTabType::iterator DstCI = ComdatSymTab.find(ComdatName); | |
| if (DstCI == ComdatSymTab.end()) { | |
| // Use the comdat if it is only available in one of the modules. | |
| LinkFromSrc = true; | |
| Result = SSK; | |
| return false; | |
| } | |
| const Comdat *DstC = &DstCI->second; | |
| Comdat::SelectionKind DSK = DstC->getSelectionKind(); | |
| return computeResultingSelectionKind(ComdatName, SSK, DSK, Result, | |
| LinkFromSrc); | |
| } | |
| bool ModuleLinker::shouldLinkFromSource(bool &LinkFromSrc, | |
| const GlobalValue &Dest, | |
| const GlobalValue &Src) { | |
| // Should we unconditionally use the Src? | |
| if (shouldOverrideFromSrc()) { | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| // We always have to add Src if it has appending linkage. | |
| if (Src.hasAppendingLinkage()) { | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| bool SrcIsDeclaration = Src.isDeclarationForLinker(); | |
| bool DestIsDeclaration = Dest.isDeclarationForLinker(); | |
| if (SrcIsDeclaration) { | |
| // If Src is external or if both Src & Dest are external.. Just link the | |
| // external globals, we aren't adding anything. | |
| if (Src.hasDLLImportStorageClass()) { | |
| // If one of GVs is marked as DLLImport, result should be dllimport'ed. | |
| LinkFromSrc = DestIsDeclaration; | |
| return false; | |
| } | |
| // If the Dest is weak, use the source linkage. | |
| if (Dest.hasExternalWeakLinkage()) { | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| // Link an available_externally over a declaration. | |
| LinkFromSrc = !Src.isDeclaration() && Dest.isDeclaration(); | |
| return false; | |
| } | |
| if (DestIsDeclaration) { | |
| // If Dest is external but Src is not: | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| if (Src.hasCommonLinkage()) { | |
| if (Dest.hasLinkOnceLinkage() || Dest.hasWeakLinkage()) { | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| if (!Dest.hasCommonLinkage()) { | |
| LinkFromSrc = false; | |
| return false; | |
| } | |
| const DataLayout &DL = Dest.getParent()->getDataLayout(); | |
| uint64_t DestSize = DL.getTypeAllocSize(Dest.getValueType()); | |
| uint64_t SrcSize = DL.getTypeAllocSize(Src.getValueType()); | |
| LinkFromSrc = SrcSize > DestSize; | |
| return false; | |
| } | |
| if (Src.isWeakForLinker()) { | |
| assert(!Dest.hasExternalWeakLinkage()); | |
| assert(!Dest.hasAvailableExternallyLinkage()); | |
| if (Dest.hasLinkOnceLinkage() && Src.hasWeakLinkage()) { | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| LinkFromSrc = false; | |
| return false; | |
| } | |
| if (Dest.isWeakForLinker()) { | |
| assert(Src.hasExternalLinkage()); | |
| LinkFromSrc = true; | |
| return false; | |
| } | |
| assert(!Src.hasExternalWeakLinkage()); | |
| assert(!Dest.hasExternalWeakLinkage()); | |
| assert(Dest.hasExternalLinkage() && Src.hasExternalLinkage() && | |
| "Unexpected linkage type!"); | |
| return emitError("Linking globals named '" + Src.getName() + | |
| "': symbol multiply defined!"); | |
| } | |
| bool ModuleLinker::linkIfNeeded(GlobalValue &GV) { | |
| GlobalValue *DGV = getLinkedToGlobal(&GV); | |
| if (shouldLinkOnlyNeeded()) { | |
| // Always import variables with appending linkage. | |
| if (!GV.hasAppendingLinkage()) { | |
| // Don't import globals unless they are referenced by the destination | |
| // module. | |
| if (!DGV) | |
| return false; | |
| // Don't import globals that are already defined in the destination module | |
| if (!DGV->isDeclaration()) | |
| return false; | |
| } | |
| } | |
| if (DGV && !GV.hasLocalLinkage() && !GV.hasAppendingLinkage()) { | |
| auto *DGVar = dyn_cast<GlobalVariable>(DGV); | |
| auto *SGVar = dyn_cast<GlobalVariable>(&GV); | |
| if (DGVar && SGVar) { | |
| if (DGVar->isDeclaration() && SGVar->isDeclaration() && | |
| (!DGVar->isConstant() || !SGVar->isConstant())) { | |
| DGVar->setConstant(false); | |
| SGVar->setConstant(false); | |
| } | |
| if (DGVar->hasCommonLinkage() && SGVar->hasCommonLinkage()) { | |
| MaybeAlign Align( | |
| std::max(DGVar->getAlignment(), SGVar->getAlignment())); | |
| SGVar->setAlignment(Align); | |
| DGVar->setAlignment(Align); | |
| } | |
| } | |
| GlobalValue::VisibilityTypes Visibility = | |
| getMinVisibility(DGV->getVisibility(), GV.getVisibility()); | |
| DGV->setVisibility(Visibility); | |
| GV.setVisibility(Visibility); | |
| GlobalValue::UnnamedAddr UnnamedAddr = GlobalValue::getMinUnnamedAddr( | |
| DGV->getUnnamedAddr(), GV.getUnnamedAddr()); | |
| DGV->setUnnamedAddr(UnnamedAddr); | |
| GV.setUnnamedAddr(UnnamedAddr); | |
| } | |
| if (!DGV && !shouldOverrideFromSrc() && | |
| (GV.hasLocalLinkage() || GV.hasLinkOnceLinkage() || | |
| GV.hasAvailableExternallyLinkage())) | |
| return false; | |
| if (GV.isDeclaration()) | |
| return false; | |
| if (const Comdat *SC = GV.getComdat()) { | |
| bool LinkFromSrc; | |
| Comdat::SelectionKind SK; | |
| std::tie(SK, LinkFromSrc) = ComdatsChosen[SC]; | |
| if (!LinkFromSrc) | |
| return false; | |
| } | |
| bool LinkFromSrc = true; | |
| if (DGV && shouldLinkFromSource(LinkFromSrc, *DGV, GV)) | |
| return true; | |
| if (LinkFromSrc) | |
| ValuesToLink.insert(&GV); | |
| return false; | |
| } | |
| void ModuleLinker::addLazyFor(GlobalValue &GV, const IRMover::ValueAdder &Add) { | |
| // Add these to the internalize list | |
| if (!GV.hasLinkOnceLinkage() && !GV.hasAvailableExternallyLinkage() && | |
| !shouldLinkOnlyNeeded()) | |
| return; | |
| if (InternalizeCallback) | |
| Internalize.insert(GV.getName()); | |
| Add(GV); | |
| const Comdat *SC = GV.getComdat(); | |
| if (!SC) | |
| return; | |
| for (GlobalValue *GV2 : LazyComdatMembers[SC]) { | |
| GlobalValue *DGV = getLinkedToGlobal(GV2); | |
| bool LinkFromSrc = true; | |
| if (DGV && shouldLinkFromSource(LinkFromSrc, *DGV, *GV2)) | |
| return; | |
| if (!LinkFromSrc) | |
| continue; | |
| if (InternalizeCallback) | |
| Internalize.insert(GV2->getName()); | |
| Add(*GV2); | |
| } | |
| } | |
| void ModuleLinker::dropReplacedComdat( | |
| GlobalValue &GV, const DenseSet<const Comdat *> &ReplacedDstComdats) { | |
| Comdat *C = GV.getComdat(); | |
| if (!C) | |
| return; | |
| if (!ReplacedDstComdats.count(C)) | |
| return; | |
| if (GV.use_empty()) { | |
| GV.eraseFromParent(); | |
| return; | |
| } | |
| if (auto *F = dyn_cast<Function>(&GV)) { | |
| F->deleteBody(); | |
| } else if (auto *Var = dyn_cast<GlobalVariable>(&GV)) { | |
| Var->setInitializer(nullptr); | |
| } else { | |
| auto &Alias = cast<GlobalAlias>(GV); | |
| Module &M = *Alias.getParent(); | |
| PointerType &Ty = *cast<PointerType>(Alias.getType()); | |
| GlobalValue *Declaration; | |
| if (auto *FTy = dyn_cast<FunctionType>(Alias.getValueType())) { | |
| Declaration = Function::Create(FTy, GlobalValue::ExternalLinkage, "", &M); | |
| } else { | |
| Declaration = | |
| new GlobalVariable(M, Ty.getElementType(), /*isConstant*/ false, | |
| GlobalValue::ExternalLinkage, | |
| /*Initializer*/ nullptr); | |
| } | |
| Declaration->takeName(&Alias); | |
| Alias.replaceAllUsesWith(Declaration); | |
| Alias.eraseFromParent(); | |
| } | |
| } | |
| bool ModuleLinker::run() { | |
| Module &DstM = Mover.getModule(); | |
| DenseSet<const Comdat *> ReplacedDstComdats; | |
| for (const auto &SMEC : SrcM->getComdatSymbolTable()) { | |
| const Comdat &C = SMEC.getValue(); | |
| if (ComdatsChosen.count(&C)) | |
| continue; | |
| Comdat::SelectionKind SK; | |
| bool LinkFromSrc; | |
| if (getComdatResult(&C, SK, LinkFromSrc)) | |
| return true; | |
| ComdatsChosen[&C] = std::make_pair(SK, LinkFromSrc); | |
| if (!LinkFromSrc) | |
| continue; | |
| Module::ComdatSymTabType &ComdatSymTab = DstM.getComdatSymbolTable(); | |
| Module::ComdatSymTabType::iterator DstCI = ComdatSymTab.find(C.getName()); | |
| if (DstCI == ComdatSymTab.end()) | |
| continue; | |
| // The source comdat is replacing the dest one. | |
| const Comdat *DstC = &DstCI->second; | |
| ReplacedDstComdats.insert(DstC); | |
| } | |
| // Alias have to go first, since we are not able to find their comdats | |
| // otherwise. | |
| for (auto I = DstM.alias_begin(), E = DstM.alias_end(); I != E;) { | |
| GlobalAlias &GV = *I++; | |
| dropReplacedComdat(GV, ReplacedDstComdats); | |
| } | |
| for (auto I = DstM.global_begin(), E = DstM.global_end(); I != E;) { | |
| GlobalVariable &GV = *I++; | |
| dropReplacedComdat(GV, ReplacedDstComdats); | |
| } | |
| for (auto I = DstM.begin(), E = DstM.end(); I != E;) { | |
| Function &GV = *I++; | |
| dropReplacedComdat(GV, ReplacedDstComdats); | |
| } | |
| for (GlobalVariable &GV : SrcM->globals()) | |
| if (GV.hasLinkOnceLinkage()) | |
| if (const Comdat *SC = GV.getComdat()) | |
| LazyComdatMembers[SC].push_back(&GV); | |
| for (Function &SF : *SrcM) | |
| if (SF.hasLinkOnceLinkage()) | |
| if (const Comdat *SC = SF.getComdat()) | |
| LazyComdatMembers[SC].push_back(&SF); | |
| for (GlobalAlias &GA : SrcM->aliases()) | |
| if (GA.hasLinkOnceLinkage()) | |
| if (const Comdat *SC = GA.getComdat()) | |
| LazyComdatMembers[SC].push_back(&GA); | |
| // Insert all of the globals in src into the DstM module... without linking | |
| // initializers (which could refer to functions not yet mapped over). | |
| for (GlobalVariable &GV : SrcM->globals()) | |
| if (linkIfNeeded(GV)) | |
| return true; | |
| for (Function &SF : *SrcM) | |
| if (linkIfNeeded(SF)) | |
| return true; | |
| for (GlobalAlias &GA : SrcM->aliases()) | |
| if (linkIfNeeded(GA)) | |
| return true; | |
| for (unsigned I = 0; I < ValuesToLink.size(); ++I) { | |
| GlobalValue *GV = ValuesToLink[I]; | |
| const Comdat *SC = GV->getComdat(); | |
| if (!SC) | |
| continue; | |
| for (GlobalValue *GV2 : LazyComdatMembers[SC]) { | |
| GlobalValue *DGV = getLinkedToGlobal(GV2); | |
| bool LinkFromSrc = true; | |
| if (DGV && shouldLinkFromSource(LinkFromSrc, *DGV, *GV2)) | |
| return true; | |
| if (LinkFromSrc) | |
| ValuesToLink.insert(GV2); | |
| } | |
| } | |
| if (InternalizeCallback) { | |
| for (GlobalValue *GV : ValuesToLink) | |
| Internalize.insert(GV->getName()); | |
| } | |
| // FIXME: Propagate Errors through to the caller instead of emitting | |
| // diagnostics. | |
| bool HasErrors = false; | |
| if (Error E = Mover.move(std::move(SrcM), ValuesToLink.getArrayRef(), | |
| [this](GlobalValue &GV, IRMover::ValueAdder Add) { | |
| addLazyFor(GV, Add); | |
| }, | |
| /* IsPerformingImport */ false)) { | |
| handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { | |
| DstM.getContext().diagnose(LinkDiagnosticInfo(DS_Error, EIB.message())); | |
| HasErrors = true; | |
| }); | |
| } | |
| if (HasErrors) | |
| return true; | |
| if (InternalizeCallback) | |
| InternalizeCallback(DstM, Internalize); | |
| return false; | |
| } | |
| Linker::Linker(Module &M) : Mover(M) {} | |
| bool Linker::linkInModule( | |
| std::unique_ptr<Module> Src, unsigned Flags, | |
| std::function<void(Module &, const StringSet<> &)> InternalizeCallback) { | |
| ModuleLinker ModLinker(Mover, std::move(Src), Flags, | |
| std::move(InternalizeCallback)); | |
| return ModLinker.run(); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // LinkModules entrypoint. | |
| //===----------------------------------------------------------------------===// | |
| /// This function links two modules together, with the resulting Dest module | |
| /// modified to be the composite of the two input modules. If an error occurs, | |
| /// true is returned and ErrorMsg (if not null) is set to indicate the problem. | |
| /// Upon failure, the Dest module could be in a modified state, and shouldn't be | |
| /// relied on to be consistent. | |
| bool Linker::linkModules( | |
| Module &Dest, std::unique_ptr<Module> Src, unsigned Flags, | |
| std::function<void(Module &, const StringSet<> &)> InternalizeCallback) { | |
| Linker L(Dest); | |
| return L.linkInModule(std::move(Src), Flags, std::move(InternalizeCallback)); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // C API. | |
| //===----------------------------------------------------------------------===// | |
| LLVMBool LLVMLinkModules2(LLVMModuleRef Dest, LLVMModuleRef Src) { | |
| Module *D = unwrap(Dest); | |
| std::unique_ptr<Module> M(unwrap(Src)); | |
| return Linker::linkModules(*D, std::move(M)); | |
| } |