#include "LinkDiagnosticInfo.h"
#include "llvm-c/Linker.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/IR/Comdat.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 {
enum class LinkFrom { Dst, Src, Both };
class ModuleLinker {
IRMover &Mover;
std::unique_ptr<Module> SrcM;
SetVector<GlobalValue *> ValuesToLink;
unsigned Flags;
StringSet<> Internalize;
std::function<void(Module &, const StringSet<> &)> InternalizeCallback;
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);
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,
LinkFrom &From);
DenseMap<const Comdat *, std::pair<Comdat::SelectionKind, LinkFrom>>
ComdatsChosen;
bool getComdatResult(const Comdat *SrcC, Comdat::SelectionKind &SK,
LinkFrom &From);
DenseMap<const Comdat *, std::vector<GlobalValue *>> LazyComdatMembers;
GlobalValue *getLinkedToGlobal(const GlobalValue *SrcGV) {
Module &DstM = Mover.getModule();
if (!SrcGV->hasName() || GlobalValue::isLocalLinkage(SrcGV->getLinkage()))
return nullptr;
GlobalValue *DGV = DstM.getNamedValue(SrcGV->getName());
if (!DGV)
return nullptr;
if (DGV->hasLocalLinkage())
return nullptr;
return DGV;
}
void dropReplacedComdat(GlobalValue &GV,
const DenseSet<const Comdat *> &ReplacedDstComdats);
bool linkIfNeeded(GlobalValue &GV, SmallVectorImpl<GlobalValue *> &GVToClone);
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->getAliaseeObject();
if (!GVal)
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,
LinkFrom &From) {
Module &DstM = Mover.getModule();
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:
From = LinkFrom::Dst;
break;
case Comdat::SelectionKind::NoDeduplicate:
From = LinkFrom::Both;
break;
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!");
From = LinkFrom::Dst;
} else if (Result == Comdat::SelectionKind::Largest) {
From = SrcSize > DstSize ? LinkFrom::Src : LinkFrom::Dst;
} else if (Result == Comdat::SelectionKind::SameSize) {
if (SrcSize != DstSize)
return emitError("Linking COMDATs named '" + ComdatName +
"': SameSize violated!");
From = LinkFrom::Dst;
} else {
llvm_unreachable("unknown selection kind");
}
break;
}
}
return false;
}
bool ModuleLinker::getComdatResult(const Comdat *SrcC,
Comdat::SelectionKind &Result,
LinkFrom &From) {
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()) {
From = LinkFrom::Src;
Result = SSK;
return false;
}
const Comdat *DstC = &DstCI->second;
Comdat::SelectionKind DSK = DstC->getSelectionKind();
return computeResultingSelectionKind(ComdatName, SSK, DSK, Result, From);
}
bool ModuleLinker::shouldLinkFromSource(bool &LinkFromSrc,
const GlobalValue &Dest,
const GlobalValue &Src) {
if (shouldOverrideFromSrc()) {
LinkFromSrc = true;
return false;
}
if (Src.hasAppendingLinkage() || Dest.hasAppendingLinkage()) {
LinkFromSrc = true;
return false;
}
bool SrcIsDeclaration = Src.isDeclarationForLinker();
bool DestIsDeclaration = Dest.isDeclarationForLinker();
if (SrcIsDeclaration) {
if (Src.hasDLLImportStorageClass()) {
LinkFromSrc = DestIsDeclaration;
return false;
}
if (Dest.hasExternalWeakLinkage()) {
LinkFromSrc = true;
return false;
}
LinkFromSrc = !Src.isDeclaration() && Dest.isDeclaration();
return false;
}
if (DestIsDeclaration) {
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,
SmallVectorImpl<GlobalValue *> &GVToClone) {
GlobalValue *DGV = getLinkedToGlobal(&GV);
if (shouldLinkOnlyNeeded()) {
if (!GV.hasAppendingLinkage()) {
if (!DGV)
return false;
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;
LinkFrom ComdatFrom = LinkFrom::Dst;
if (const Comdat *SC = GV.getComdat()) {
std::tie(std::ignore, ComdatFrom) = ComdatsChosen[SC];
if (ComdatFrom == LinkFrom::Dst)
return false;
}
bool LinkFromSrc = true;
if (DGV && shouldLinkFromSource(LinkFromSrc, *DGV, GV))
return true;
if (DGV && ComdatFrom == LinkFrom::Both)
GVToClone.push_back(LinkFromSrc ? DGV : &GV);
if (LinkFromSrc)
ValuesToLink.insert(&GV);
return false;
}
void ModuleLinker::addLazyFor(GlobalValue &GV, const IRMover::ValueAdder &Add) {
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();
GlobalValue *Declaration;
if (auto *FTy = dyn_cast<FunctionType>(Alias.getValueType())) {
Declaration = Function::Create(FTy, GlobalValue::ExternalLinkage, "", &M);
} else {
Declaration =
new GlobalVariable(M, Alias.getValueType(), false,
GlobalValue::ExternalLinkage,
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;
LinkFrom From;
if (getComdatResult(&C, SK, From))
return true;
ComdatsChosen[&C] = std::make_pair(SK, From);
if (From != LinkFrom::Src)
continue;
Module::ComdatSymTabType &ComdatSymTab = DstM.getComdatSymbolTable();
Module::ComdatSymTabType::iterator DstCI = ComdatSymTab.find(C.getName());
if (DstCI == ComdatSymTab.end())
continue;
const Comdat *DstC = &DstCI->second;
ReplacedDstComdats.insert(DstC);
}
for (GlobalAlias &GV : llvm::make_early_inc_range(DstM.aliases()))
dropReplacedComdat(GV, ReplacedDstComdats);
for (GlobalVariable &GV : llvm::make_early_inc_range(DstM.globals()))
dropReplacedComdat(GV, ReplacedDstComdats);
for (Function &GV : llvm::make_early_inc_range(DstM))
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);
SmallVector<GlobalValue *, 0> GVToClone;
for (GlobalVariable &GV : SrcM->globals())
if (linkIfNeeded(GV, GVToClone))
return true;
for (Function &SF : *SrcM)
if (linkIfNeeded(SF, GVToClone))
return true;
for (GlobalAlias &GA : SrcM->aliases())
if (linkIfNeeded(GA, GVToClone))
return true;
for (GlobalIFunc &GI : SrcM->ifuncs())
if (linkIfNeeded(GI, GVToClone))
return true;
for (GlobalValue *GV : GVToClone) {
if (auto *Var = dyn_cast<GlobalVariable>(GV)) {
auto *NewVar = new GlobalVariable(*Var->getParent(), Var->getValueType(),
Var->isConstant(), Var->getLinkage(),
Var->getInitializer());
NewVar->copyAttributesFrom(Var);
NewVar->setVisibility(GlobalValue::DefaultVisibility);
NewVar->setLinkage(GlobalValue::PrivateLinkage);
NewVar->setDSOLocal(true);
NewVar->setComdat(Var->getComdat());
if (Var->getParent() != &Mover.getModule())
ValuesToLink.insert(NewVar);
} else {
emitError("linking '" + GV->getName() +
"': non-variables in comdat nodeduplicate are not handled");
}
}
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());
}
bool HasErrors = false;
if (Error E =
Mover.move(std::move(SrcM), ValuesToLink.getArrayRef(),
IRMover::LazyCallback(
[this](GlobalValue &GV, IRMover::ValueAdder Add) {
addLazyFor(GV, Add);
}),
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();
}
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));
}
LLVMBool LLVMLinkModules2(LLVMModuleRef Dest, LLVMModuleRef Src) {
Module *D = unwrap(Dest);
std::unique_ptr<Module> M(unwrap(Src));
return Linker::linkModules(*D, std::move(M));
}