Skip to content

Commit

Permalink
[Inliner] Don't removeDeadConstantUsers() when checking if a function…
Browse files Browse the repository at this point in the history
… is dead

If a function has many uses, this can take a good chunk of compile times.

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D117236
  • Loading branch information
aeubanks committed Jan 13, 2022
1 parent 00e0de0 commit 757e044
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 24 deletions.
8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/Constant.h
Expand Up @@ -204,6 +204,12 @@ class Constant : public User {
/// Constant::removeDeadConstantUsers, but doesn't remove dead constants.
bool hasOneLiveUse() const;

/// Return true if the constant has no live uses.
///
/// This returns the same result as calling Value::use_empty after
/// Constant::removeDeadConstantUsers, but doesn't remove dead constants.
bool hasZeroLiveUses() const;

const Constant *stripPointerCasts() const {
return cast<Constant>(Value::stripPointerCasts());
}
Expand Down Expand Up @@ -244,6 +250,8 @@ class Constant : public User {

/// Determine what potential relocations may be needed by this constant.
PossibleRelocationsTy getRelocationInfo() const;

bool hasNLiveUses(unsigned N) const;
};

} // end namespace llvm
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Analysis/LazyCallGraph.cpp
Expand Up @@ -1503,7 +1503,7 @@ void LazyCallGraph::removeEdge(Node &SourceN, Node &TargetN) {
void LazyCallGraph::removeDeadFunction(Function &F) {
// FIXME: This is unnecessarily restrictive. We should be able to remove
// functions which recursively call themselves.
assert(F.use_empty() &&
assert(F.hasZeroLiveUses() &&
"This routine should only be called on trivially dead functions!");

// We shouldn't remove library functions as they are never really dead while
Expand Down
14 changes: 9 additions & 5 deletions llvm/lib/IR/Constants.cpp
Expand Up @@ -779,18 +779,22 @@ void Constant::removeDeadConstantUsers() const {
}
}

bool Constant::hasOneLiveUse() const {
bool Constant::hasOneLiveUse() const { return hasNLiveUses(1); }

bool Constant::hasZeroLiveUses() const { return hasNLiveUses(0); }

bool Constant::hasNLiveUses(unsigned N) const {
unsigned NumUses = 0;
for (const Use &use : uses()) {
const Constant *User = dyn_cast<Constant>(use.getUser());
for (const Use &U : uses()) {
const Constant *User = dyn_cast<Constant>(U.getUser());
if (!User || !constantIsDead(User, /* RemoveDeadUsers= */ false)) {
++NumUses;

if (NumUses > 1)
if (NumUses > N)
return false;
}
}
return NumUses == 1;
return NumUses == N;
}

Constant *Constant::replaceUndefsWith(Constant *C, Constant *Replacement) {
Expand Down
32 changes: 14 additions & 18 deletions llvm/lib/Transforms/IPO/Inliner.cpp
Expand Up @@ -940,24 +940,20 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
// which may reduce the number of callers of other functions to one,
// changing inline cost thresholds.
bool CalleeWasDeleted = false;
if (Callee.hasLocalLinkage()) {
// To check this we also need to nuke any dead constant uses (perhaps
// made dead by this operation on other functions).
Callee.removeDeadConstantUsers();
if (Callee.use_empty() && !CG.isLibFunction(Callee)) {
Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
return Call.first->getCaller() == &Callee;
});
// Clear the body and queue the function itself for deletion when we
// finish inlining and call graph updates.
// Note that after this point, it is an error to do anything other
// than use the callee's address or delete it.
Callee.dropAllReferences();
assert(!is_contained(DeadFunctions, &Callee) &&
"Cannot put cause a function to become dead twice!");
DeadFunctions.push_back(&Callee);
CalleeWasDeleted = true;
}
if (Callee.hasLocalLinkage() && Callee.hasZeroLiveUses() &&
!CG.isLibFunction(Callee)) {
Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
return Call.first->getCaller() == &Callee;
});
// Clear the body and queue the function itself for deletion when we
// finish inlining and call graph updates.
// Note that after this point, it is an error to do anything other
// than use the callee's address or delete it.
Callee.dropAllReferences();
assert(!is_contained(DeadFunctions, &Callee) &&
"Cannot put cause a function to become dead twice!");
DeadFunctions.push_back(&Callee);
CalleeWasDeleted = true;
}
if (CalleeWasDeleted)
Advice->recordInliningWithCalleeDeleted();
Expand Down

0 comments on commit 757e044

Please sign in to comment.