mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-27 05:30:29 +00:00
[MergeFuncs] Improve ordering of equal functions
Summary: MergeFunctions currently tries to process strong functions before weak functions, because weak functions can simply call strong functions, while a strong/weak function cannot call a weak function (a backing strong function is needed). This patch additionally tries to process external functions before local functions, because we definitely have to keep the external function, but may be able to drop the local one (and definitely can if it is also unnamed_addr). Unfortunately, this exposes an existing bug in the implementation: The FnTree and FNodesInTree structures can currently go out of sync in the case where two weak functions are merged, because the function in FnTree/FNodesInTree is RAUWed. This leaves it behind in FnTree (this is intended, as it is the strong backing function which should be used for further merges), while it is replaced in FNodesInTree (this is not intended). This is fixed by switching FNodesInTree from using a ValueMap to using a DenseMap of AssertingVH. This exposes another minor issue: Currently FNodesInTree is not cleared after MergeFunctions finishes running. Currently, this is potentially dangerous (e.g. if something else wants to RAUW a function with a non-function), but at the very least it is unnecessary/inefficient. After the change to use AssertingVH it becomes more problematic, because there are certainly passes that remove functions. This issue is fixed by clearing FNodesInTree at the end of the pass. Reviewers: jfb, whitequark Reviewed By: whitequark Subscribers: rkruppe, llvm-commits Differential Revision: https://reviews.llvm.org/D53271 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@346386 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
17045f0418
commit
19a9621221
@ -284,7 +284,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 +425,7 @@ bool MergeFunctions::runOnModule(Module &M) {
|
||||
} while (!Deferred.empty());
|
||||
|
||||
FnTree.clear();
|
||||
FNodesInTree.clear();
|
||||
GlobalNumbers.clear();
|
||||
|
||||
return Changed;
|
||||
@ -817,6 +818,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) {
|
||||
@ -833,14 +852,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);
|
||||
|
55
test/Transforms/MergeFunc/external-before-local.ll
Normal file
55
test/Transforms/MergeFunc/external-before-local.ll
Normal file
@ -0,0 +1,55 @@
|
||||
; RUN: opt -S -mergefunc < %s | FileCheck %s
|
||||
|
||||
; We should normalize to test2 rather than test1,
|
||||
; because it allows us to drop test1 entirely
|
||||
|
||||
; CHECK-NOT: define internal void @test1() unnamed_addr
|
||||
; CHECK: define void @test3() unnamed_addr
|
||||
; CHECK-NEXT: call void @test2()
|
||||
; CHECK-NEXT: call void @test2()
|
||||
|
||||
declare void @dummy()
|
||||
|
||||
define internal void @test1() unnamed_addr {
|
||||
call void @dummy()
|
||||
call void @dummy()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test2() unnamed_addr {
|
||||
call void @dummy()
|
||||
call void @dummy()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test3() unnamed_addr {
|
||||
call void @test1()
|
||||
call void @test2()
|
||||
ret void
|
||||
}
|
||||
|
||||
; We should normalize to the existing test6 rather than
|
||||
; to a new anonymous strong backing function
|
||||
|
||||
; CHECK: define weak void @test5()
|
||||
; CHECK-NEXT: tail call void @test6()
|
||||
; CHECK: define weak void @test4()
|
||||
; CHECK-NEXT: tail call void @test6()
|
||||
|
||||
declare void @dummy2()
|
||||
|
||||
define weak void @test4() {
|
||||
call void @dummy2()
|
||||
call void @dummy2()
|
||||
ret void
|
||||
}
|
||||
define weak void @test5() {
|
||||
call void @dummy2()
|
||||
call void @dummy2()
|
||||
ret void
|
||||
}
|
||||
define void @test6() {
|
||||
call void @dummy2()
|
||||
call void @dummy2()
|
||||
ret void
|
||||
}
|
Loading…
Reference in New Issue
Block a user