summaryrefslogtreecommitdiff
path: root/lib/Transforms/IPO/MergeFunctions.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Transforms/IPO/MergeFunctions.cpp
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'lib/Transforms/IPO/MergeFunctions.cpp')
-rw-r--r--lib/Transforms/IPO/MergeFunctions.cpp130
1 files changed, 101 insertions, 29 deletions
diff --git a/lib/Transforms/IPO/MergeFunctions.cpp b/lib/Transforms/IPO/MergeFunctions.cpp
index 3bebb96c6d35..11efe95b10d4 100644
--- a/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/lib/Transforms/IPO/MergeFunctions.cpp
@@ -136,6 +136,7 @@ using namespace llvm;
STATISTIC(NumFunctionsMerged, "Number of functions merged");
STATISTIC(NumThunksWritten, "Number of thunks generated");
+STATISTIC(NumAliasesWritten, "Number of aliases generated");
STATISTIC(NumDoubleWeak, "Number of new functions created");
static cl::opt<unsigned> NumFunctionsForSanityCheck(
@@ -165,6 +166,11 @@ static cl::opt<bool>
cl::desc("Preserve debug info in thunk when mergefunc "
"transformations are made."));
+static cl::opt<bool>
+ MergeFunctionsAliases("mergefunc-use-aliases", cl::Hidden,
+ cl::init(false),
+ cl::desc("Allow mergefunc to create aliases"));
+
namespace {
class FunctionNode {
@@ -272,6 +278,13 @@ private:
/// delete G.
void writeThunk(Function *F, Function *G);
+ // Replace G with an alias to F (deleting function G)
+ void writeAlias(Function *F, Function *G);
+
+ // Replace G with an alias to F if possible, or a thunk to F if
+ // profitable. Returns false if neither is the case.
+ bool writeThunkOrAlias(Function *F, Function *G);
+
/// Replace function F with function G in the function tree.
void replaceFunctionInTree(const FunctionNode &FN, Function *G);
@@ -284,7 +297,7 @@ private:
// modified, i.e. in insert(), remove(), and replaceFunctionInTree(), to avoid
// dangling iterators into FnTree. The invariant that preserves this is that
// there is exactly one mapping F -> FN for each FunctionNode FN in FnTree.
- ValueMap<Function*, FnTreeType::iterator> FNodesInTree;
+ DenseMap<AssertingVH<Function>, FnTreeType::iterator> FNodesInTree;
};
} // end anonymous namespace
@@ -425,6 +438,7 @@ bool MergeFunctions::runOnModule(Module &M) {
} while (!Deferred.empty());
FnTree.clear();
+ FNodesInTree.clear();
GlobalNumbers.clear();
return Changed;
@@ -460,7 +474,7 @@ void MergeFunctions::replaceDirectCallers(Function *Old, Function *New) {
NewPAL.getRetAttributes(),
NewArgAttrs));
- remove(CS.getInstruction()->getParent()->getParent());
+ remove(CS.getInstruction()->getFunction());
U->set(BitcastNew);
}
}
@@ -608,7 +622,7 @@ void MergeFunctions::filterInstsUnrelatedToPDI(
LLVM_DEBUG(BI->print(dbgs()));
LLVM_DEBUG(dbgs() << "\n");
}
- } else if (dyn_cast<TerminatorInst>(BI) == GEntryBlock->getTerminator()) {
+ } else if (BI->isTerminator() && &*BI == GEntryBlock->getTerminator()) {
LLVM_DEBUG(dbgs() << " Will Include Terminator: ");
LLVM_DEBUG(BI->print(dbgs()));
LLVM_DEBUG(dbgs() << "\n");
@@ -679,8 +693,8 @@ void MergeFunctions::writeThunk(Function *F, Function *G) {
GEntryBlock->getTerminator()->eraseFromParent();
BB = GEntryBlock;
} else {
- NewG = Function::Create(G->getFunctionType(), G->getLinkage(), "",
- G->getParent());
+ NewG = Function::Create(G->getFunctionType(), G->getLinkage(),
+ G->getAddressSpace(), "", G->getParent());
BB = BasicBlock::Create(F->getContext(), "", NewG);
}
@@ -734,27 +748,76 @@ void MergeFunctions::writeThunk(Function *F, Function *G) {
++NumThunksWritten;
}
+// Whether this function may be replaced by an alias
+static bool canCreateAliasFor(Function *F) {
+ if (!MergeFunctionsAliases || !F->hasGlobalUnnamedAddr())
+ return false;
+
+ // We should only see linkages supported by aliases here
+ assert(F->hasLocalLinkage() || F->hasExternalLinkage()
+ || F->hasWeakLinkage() || F->hasLinkOnceLinkage());
+ return true;
+}
+
+// Replace G with an alias to F (deleting function G)
+void MergeFunctions::writeAlias(Function *F, Function *G) {
+ Constant *BitcastF = ConstantExpr::getBitCast(F, G->getType());
+ PointerType *PtrType = G->getType();
+ auto *GA = GlobalAlias::create(
+ PtrType->getElementType(), PtrType->getAddressSpace(),
+ G->getLinkage(), "", BitcastF, G->getParent());
+
+ F->setAlignment(std::max(F->getAlignment(), G->getAlignment()));
+ GA->takeName(G);
+ GA->setVisibility(G->getVisibility());
+ GA->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+
+ removeUsers(G);
+ G->replaceAllUsesWith(GA);
+ G->eraseFromParent();
+
+ LLVM_DEBUG(dbgs() << "writeAlias: " << GA->getName() << '\n');
+ ++NumAliasesWritten;
+}
+
+// Replace G with an alias to F if possible, or a thunk to F if
+// profitable. Returns false if neither is the case.
+bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
+ if (canCreateAliasFor(G)) {
+ writeAlias(F, G);
+ return true;
+ }
+ if (isThunkProfitable(F)) {
+ writeThunk(F, G);
+ return true;
+ }
+ return false;
+}
+
// Merge two equivalent functions. Upon completion, Function G is deleted.
void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
if (F->isInterposable()) {
assert(G->isInterposable());
- if (!isThunkProfitable(F)) {
+ // Both writeThunkOrAlias() calls below must succeed, either because we can
+ // create aliases for G and NewF, or because a thunk for F is profitable.
+ // F here has the same signature as NewF below, so that's what we check.
+ if (!isThunkProfitable(F) && (!canCreateAliasFor(F) || !canCreateAliasFor(G))) {
return;
}
// Make them both thunks to the same internal function.
- Function *H = Function::Create(F->getFunctionType(), F->getLinkage(), "",
- F->getParent());
- H->copyAttributesFrom(F);
- H->takeName(F);
+ Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
+ F->getAddressSpace(), "", F->getParent());
+ NewF->copyAttributesFrom(F);
+ NewF->takeName(F);
removeUsers(F);
- F->replaceAllUsesWith(H);
+ F->replaceAllUsesWith(NewF);
- unsigned MaxAlignment = std::max(G->getAlignment(), H->getAlignment());
+ unsigned MaxAlignment = std::max(G->getAlignment(), NewF->getAlignment());
- writeThunk(F, G);
- writeThunk(F, H);
+ writeThunkOrAlias(F, G);
+ writeThunkOrAlias(F, NewF);
F->setAlignment(MaxAlignment);
F->setLinkage(GlobalValue::PrivateLinkage);
@@ -770,6 +833,7 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
GlobalNumbers.erase(G);
// If G's address is not significant, replace it entirely.
Constant *BitcastF = ConstantExpr::getBitCast(F, G->getType());
+ removeUsers(G);
G->replaceAllUsesWith(BitcastF);
} else {
// Redirect direct callers of G to F. (See note on MergeFunctionsPDI
@@ -781,18 +845,15 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
// If G was internal then we may have replaced all uses of G with F. If so,
// stop here and delete G. There's no need for a thunk. (See note on
// MergeFunctionsPDI above).
- if (G->hasLocalLinkage() && G->use_empty() && !MergeFunctionsPDI) {
+ if (G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI) {
G->eraseFromParent();
++NumFunctionsMerged;
return;
}
- if (!isThunkProfitable(F)) {
- return;
+ if (writeThunkOrAlias(F, G)) {
+ ++NumFunctionsMerged;
}
-
- writeThunk(F, G);
- ++NumFunctionsMerged;
}
}
@@ -816,6 +877,24 @@ void MergeFunctions::replaceFunctionInTree(const FunctionNode &FN,
FN.replaceBy(G);
}
+// Ordering for functions that are equal under FunctionComparator
+static bool isFuncOrderCorrect(const Function *F, const Function *G) {
+ if (F->isInterposable() != G->isInterposable()) {
+ // Strong before weak, because the weak function may call the strong
+ // one, but not the other way around.
+ return !F->isInterposable();
+ }
+ if (F->hasLocalLinkage() != G->hasLocalLinkage()) {
+ // External before local, because we definitely have to keep the external
+ // function, but may be able to drop the local one.
+ return !F->hasLocalLinkage();
+ }
+ // Impose a total order (by name) on the replacement of functions. This is
+ // important when operating on more than one module independently to prevent
+ // cycles of thunks calling each other when the modules are linked together.
+ return F->getName() <= G->getName();
+}
+
// Insert a ComparableFunction into the FnTree, or merge it away if equal to one
// that was already inserted.
bool MergeFunctions::insert(Function *NewFunction) {
@@ -832,14 +911,7 @@ bool MergeFunctions::insert(Function *NewFunction) {
const FunctionNode &OldF = *Result.first;
- // Impose a total order (by name) on the replacement of functions. This is
- // important when operating on more than one module independently to prevent
- // cycles of thunks calling each other when the modules are linked together.
- //
- // First of all, we process strong functions before weak functions.
- if ((OldF.getFunc()->isInterposable() && !NewFunction->isInterposable()) ||
- (OldF.getFunc()->isInterposable() == NewFunction->isInterposable() &&
- OldF.getFunc()->getName() > NewFunction->getName())) {
+ if (!isFuncOrderCorrect(OldF.getFunc(), NewFunction)) {
// Swap the two functions.
Function *F = OldF.getFunc();
replaceFunctionInTree(*Result.first, NewFunction);
@@ -882,7 +954,7 @@ void MergeFunctions::removeUsers(Value *V) {
for (User *U : V->users()) {
if (Instruction *I = dyn_cast<Instruction>(U)) {
- remove(I->getParent()->getParent());
+ remove(I->getFunction());
} else if (isa<GlobalValue>(U)) {
// do nothing
} else if (Constant *C = dyn_cast<Constant>(U)) {