summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/Transforms/Utils/ModuleUtils.h16
-rw-r--r--llvm/lib/Transforms/IPO/AlwaysInliner.cpp28
-rw-r--r--llvm/lib/Transforms/IPO/Inliner.cpp40
-rw-r--r--llvm/lib/Transforms/Utils/ModuleUtils.cpp64
-rw-r--r--llvm/test/Transforms/Inline/always-inline.ll27
5 files changed, 137 insertions, 38 deletions
diff --git a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
index ca5750cf68d..27508799f8e 100644
--- a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
@@ -65,6 +65,22 @@ void appendToUsed(Module &M, ArrayRef<GlobalValue *> Values);
/// \brief Adds global values to the llvm.compiler.used list.
void appendToCompilerUsed(Module &M, ArrayRef<GlobalValue *> Values);
+/// Filter out potentially dead comdat functions where other entries keep the
+/// entire comdat group alive.
+///
+/// This is designed for cases where functions appear to become dead but remain
+/// alive due to other live entries in their comdat group.
+///
+/// The \p DeadComdatFunctions container should only have pointers to
+/// `Function`s which are members of a comdat group and are believed to be
+/// dead.
+///
+/// After this routine finishes, the only remaining `Function`s in \p
+/// DeadComdatFunctions are those where every member of the comdat is listed
+/// and thus removing them is safe (provided *all* are removed).
+void filterDeadComdatFunctions(
+ Module &M, SmallVectorImpl<Function *> &DeadComdatFunctions);
+
} // End llvm namespace
#endif // LLVM_TRANSFORMS_UTILS_MODULEUTILS_H
diff --git a/llvm/lib/Transforms/IPO/AlwaysInliner.cpp b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp
index 1a3f8ced465..b7d96007c24 100644
--- a/llvm/lib/Transforms/IPO/AlwaysInliner.cpp
+++ b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp
@@ -29,6 +29,7 @@
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/Inliner.h"
#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
@@ -60,13 +61,26 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M, ModuleAnalysisManager &) {
InlinedFunctions.push_back(&F);
}
- // Now try to delete all the functions we inlined.
- for (Function *InlinedF : InlinedFunctions) {
- InlinedF->removeDeadConstantUsers();
- // FIXME: We should use some utility to handle cases where we can
- // completely remove the comdat.
- if (InlinedF->isDefTriviallyDead() && !InlinedF->hasComdat())
- M.getFunctionList().erase(InlinedF);
+ // Remove any live functions.
+ erase_if(InlinedFunctions, [&](Function *F) {
+ F->removeDeadConstantUsers();
+ return !F->isDefTriviallyDead();
+ });
+
+ // Delete the non-comdat ones from the module and also from our vector.
+ auto NonComdatBegin = partition(
+ InlinedFunctions, [&](Function *F) { return F->hasComdat(); });
+ for (Function *F : make_range(NonComdatBegin, InlinedFunctions.end()))
+ M.getFunctionList().erase(F);
+ InlinedFunctions.erase(NonComdatBegin, InlinedFunctions.end());
+
+ if (!InlinedFunctions.empty()) {
+ // Now we just have the comdat functions. Filter out the ones whose comdats
+ // are not actually dead.
+ filterDeadComdatFunctions(M, InlinedFunctions);
+ // The remaining functions are actually dead.
+ for (Function *F : InlinedFunctions)
+ M.getFunctionList().erase(F);
}
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
diff --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp
index 7b2db949d5a..ff534a19e53 100644
--- a/llvm/lib/Transforms/IPO/Inliner.cpp
+++ b/llvm/lib/Transforms/IPO/Inliner.cpp
@@ -35,6 +35,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
#define DEBUG_TYPE "inline"
@@ -668,8 +669,7 @@ bool LegacyInlinerBase::doFinalization(CallGraph &CG) {
bool LegacyInlinerBase::removeDeadFunctions(CallGraph &CG,
bool AlwaysInlineOnly) {
SmallVector<CallGraphNode *, 16> FunctionsToRemove;
- SmallVector<CallGraphNode *, 16> DeadFunctionsInComdats;
- SmallDenseMap<const Comdat *, int, 16> ComdatEntriesAlive;
+ SmallVector<Function *, 16> DeadFunctionsInComdats;
auto RemoveCGN = [&](CallGraphNode *CGN) {
// Remove any call graph edges from the function to its callees.
@@ -710,9 +710,8 @@ bool LegacyInlinerBase::removeDeadFunctions(CallGraph &CG,
// The inliner doesn't visit non-function entities which are in COMDAT
// groups so it is unsafe to do so *unless* the linkage is local.
if (!F->hasLocalLinkage()) {
- if (const Comdat *C = F->getComdat()) {
- --ComdatEntriesAlive[C];
- DeadFunctionsInComdats.push_back(CGN);
+ if (F->hasComdat()) {
+ DeadFunctionsInComdats.push_back(F);
continue;
}
}
@@ -720,32 +719,11 @@ bool LegacyInlinerBase::removeDeadFunctions(CallGraph &CG,
RemoveCGN(CGN);
}
if (!DeadFunctionsInComdats.empty()) {
- // Count up all the entities in COMDAT groups
- auto ComdatGroupReferenced = [&](const Comdat *C) {
- auto I = ComdatEntriesAlive.find(C);
- if (I != ComdatEntriesAlive.end())
- ++(I->getSecond());
- };
- for (const Function &F : CG.getModule())
- if (const Comdat *C = F.getComdat())
- ComdatGroupReferenced(C);
- for (const GlobalVariable &GV : CG.getModule().globals())
- if (const Comdat *C = GV.getComdat())
- ComdatGroupReferenced(C);
- for (const GlobalAlias &GA : CG.getModule().aliases())
- if (const Comdat *C = GA.getComdat())
- ComdatGroupReferenced(C);
- for (CallGraphNode *CGN : DeadFunctionsInComdats) {
- Function *F = CGN->getFunction();
- const Comdat *C = F->getComdat();
- int NumAlive = ComdatEntriesAlive[C];
- // We can remove functions in a COMDAT group if the entire group is dead.
- assert(NumAlive >= 0);
- if (NumAlive > 0)
- continue;
-
- RemoveCGN(CGN);
- }
+ // Filter out the functions whose comdats remain alive.
+ filterDeadComdatFunctions(CG.getModule(), DeadFunctionsInComdats);
+ // Remove the rest.
+ for (Function *F : DeadFunctionsInComdats)
+ RemoveCGN(CG[F]);
}
if (FunctionsToRemove.empty())
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index be9b77b7771..0d623df77a6 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -164,3 +164,67 @@ std::pair<Function *, Function *> llvm::createSanitizerCtorAndInitFunctions(
}
return std::make_pair(Ctor, InitFunction);
}
+
+void llvm::filterDeadComdatFunctions(
+ Module &M, SmallVectorImpl<Function *> &DeadComdatFunctions) {
+ // Build a map from the comdat to the number of entries in that comdat we
+ // think are dead. If this fully covers the comdat group, then the entire
+ // group is dead. If we find another entry in the comdat group though, we'll
+ // have to preserve the whole group.
+ SmallDenseMap<Comdat *, int, 16> ComdatEntriesCovered;
+ for (Function *F : DeadComdatFunctions) {
+ Comdat *C = F->getComdat();
+ assert(C && "Expected all input GVs to be in a comdat!");
+ ComdatEntriesCovered[C] += 1;
+ }
+
+ auto CheckComdat = [&](Comdat &C) {
+ auto CI = ComdatEntriesCovered.find(&C);
+ if (CI == ComdatEntriesCovered.end())
+ return;
+
+ // If this could have been covered by a dead entry, just subtract one to
+ // account for it.
+ if (CI->second > 0) {
+ CI->second -= 1;
+ return;
+ }
+
+ // If we've already accounted for all the entries that were dead, the
+ // entire comdat is alive so remove it from the map.
+ ComdatEntriesCovered.erase(CI);
+ };
+
+ auto CheckAllComdats = [&] {
+ for (Function &F : M.functions())
+ if (Comdat *C = F.getComdat()) {
+ CheckComdat(*C);
+ if (ComdatEntriesCovered.empty())
+ return;
+ }
+ for (GlobalVariable &GV : M.globals())
+ if (Comdat *C = GV.getComdat()) {
+ CheckComdat(*C);
+ if (ComdatEntriesCovered.empty())
+ return;
+ }
+ for (GlobalAlias &GA : M.aliases())
+ if (Comdat *C = GA.getComdat()) {
+ CheckComdat(*C);
+ if (ComdatEntriesCovered.empty())
+ return;
+ }
+ };
+ CheckAllComdats();
+
+ if (ComdatEntriesCovered.empty()) {
+ DeadComdatFunctions.clear();
+ return;
+ }
+
+ // Remove the entries that were not covering.
+ erase_if(DeadComdatFunctions, [&](GlobalValue *GV) {
+ return ComdatEntriesCovered.find(GV->getComdat()) ==
+ ComdatEntriesCovered.end();
+ });
+}
diff --git a/llvm/test/Transforms/Inline/always-inline.ll b/llvm/test/Transforms/Inline/always-inline.ll
index e8322410b74..5366b5a16cc 100644
--- a/llvm/test/Transforms/Inline/always-inline.ll
+++ b/llvm/test/Transforms/Inline/always-inline.ll
@@ -278,3 +278,30 @@ entry:
ret void
; CHECK: ret void
}
+
+; The 'inner13*' and 'outer13' functions test that we do remove functions
+; which are part of a comdat group where all of the members are removed during
+; always inlining.
+$comdat13 = comdat any
+
+define linkonce void @inner13a() alwaysinline comdat($comdat13) {
+; CHECK-NOT: @inner13a(
+ ret void
+}
+
+define linkonce void @inner13b() alwaysinline comdat($comdat13) {
+; CHECK-NOT: @inner13b(
+ ret void
+}
+
+define void @outer13() {
+; CHECK-LABEL: @outer13(
+entry:
+ call void @inner13a()
+ call void @inner13b()
+; CHECK-NOT: call void @inner13a
+; CHECK-NOT: call void @inner13b
+
+ ret void
+; CHECK: ret void
+}
OpenPOWER on IntegriCloud