void exploreFromEntry(Attributor &A, const Function *F) {
ToBeExploredPaths.insert(&(F->getEntryBlock().front()));
- AssumedLiveBlocks.insert(&(F->getEntryBlock()));
+ assumeLive(A, F->getEntryBlock());
for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
if (const Instruction *NextNoReturnI =
}
if (SplitPos == &NormalDestBB->front())
- AssumedLiveBlocks.insert(NormalDestBB);
+ assumeLive(A, *NormalDestBB);
}
BB = SplitPos->getParent();
return F.hasPersonalityFn() && !canSimplifyInvokeNoUnwind(&F);
}
+ /// Assume \p BB is (partially) live now and indicate to the Attributor \p A
+ /// that internal function called from \p BB should now be looked at.
+ void assumeLive(Attributor &A, const BasicBlock &BB) {
+ if (!AssumedLiveBlocks.insert(&BB).second)
+ return;
+
+ // We assume that all of BB is (probably) live now and if there are calls to
+ // internal functions we will assume that those are now live as well. This
+ // is a performance optimization for blocks with calls to a lot of internal
+ // functions. It can however cause dead functions to be treated as live.
+ for (const Instruction &I : BB)
+ if (ImmutableCallSite ICS = ImmutableCallSite(&I))
+ if (const Function *F = ICS.getCalledFunction())
+ if (F->hasInternalLinkage())
+ A.markLiveInternalFunction(*F);
+ }
+
/// Collection of to be explored paths.
SmallSetVector<const Instruction *, 8> ToBeExploredPaths;
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
- STATS_DECL(
- DeadInternalFunction, Function,
- "Number of internal functions classified as dead (no live callsite)");
- BUILD_STAT_NAME(DeadInternalFunction, Function) +=
- (getAssociatedFunction()->hasInternalLinkage() &&
- AssumedLiveBlocks.empty())
- ? 1
- : 0;
- STATS_DECL(DeadBlocks, Function,
- "Number of basic blocks classified as dead");
- BUILD_STAT_NAME(DeadBlocks, Function) +=
- getAssociatedFunction()->size() - AssumedLiveBlocks.size();
STATS_DECL(PartiallyDeadBlocks, Function,
"Number of basic blocks classified as partially dead");
BUILD_STAT_NAME(PartiallyDeadBlocks, Function) += NoReturnCalls.size();
// Use nounwind to justify the unwind block is dead as well.
const auto &AANoUnw = A.getAAFor<AANoUnwind>(*this, IPos);
if (!Invoke2CallAllowed || !AANoUnw.isAssumedNoUnwind()) {
- AssumedLiveBlocks.insert(Invoke->getUnwindDest());
+ assumeLive(A, *Invoke->getUnwindDest());
ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front());
}
}
// get new paths (reachable blocks).
for (const BasicBlock *SuccBB : successors(BB)) {
- AssumedLiveBlocks.insert(SuccBB);
+ assumeLive(A, *SuccBB);
ToBeExploredPaths.insert(&SuccBB->front());
}
}
ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) {
- const Function *F = getAssociatedFunction();
ChangeStatus Status = ChangeStatus::UNCHANGED;
- if (F->hasInternalLinkage() && AssumedLiveBlocks.empty()) {
- auto CallSiteCheck = [&](CallSite) { return false; };
-
- // All callsites of F are dead.
- if (A.checkForAllCallSites(CallSiteCheck, *this, true))
- return ChangeStatus::UNCHANGED;
-
- // There exists at least one live call site, so we explore the function.
- Status = ChangeStatus::CHANGED;
-
- exploreFromEntry(A, F);
- }
-
// Temporary collection to iterate over existing noreturn instructions. This
// will alow easier modification of NoReturnCalls collection
SmallVector<const Instruction *, 8> NoReturnChanged;
return true;
}
-ChangeStatus Attributor::run() {
- // Initialize all abstract attributes, allow new ones to be created.
- for (unsigned u = 0; u < AllAbstractAttributes.size(); u++)
- AllAbstractAttributes[u]->initialize(*this);
-
+ChangeStatus Attributor::run(Module &M) {
LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized "
<< AllAbstractAttributes.size()
<< " abstract attributes.\n");
if (VerifyAttributor && FinishedAtFixpoint &&
ManifestChange == ChangeStatus::CHANGED) {
VerifyAttributor = false;
- ChangeStatus VerifyStatus = run();
+ ChangeStatus VerifyStatus = run(M);
if (VerifyStatus != ChangeStatus::UNCHANGED)
llvm_unreachable(
"Attributor verification failed, re-run did result in an IR change "
"Expected the final number of abstract attributes to remain unchanged!");
// Delete stuff at the end to avoid invalid references and a nice order.
- LLVM_DEBUG(dbgs() << "\n[Attributor] Delete " << ToBeDeletedFunctions.size()
- << " functions and " << ToBeDeletedBlocks.size()
- << " blocks and " << ToBeDeletedInsts.size()
- << " instructions\n");
- for (Instruction *I : ToBeDeletedInsts) {
- if (!I->use_empty())
- I->replaceAllUsesWith(UndefValue::get(I->getType()));
- I->eraseFromParent();
- }
+ {
+ LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least "
+ << ToBeDeletedFunctions.size() << " functions and "
+ << ToBeDeletedBlocks.size() << " blocks and "
+ << ToBeDeletedInsts.size() << " instructions\n");
+ for (Instruction *I : ToBeDeletedInsts) {
+ if (!I->use_empty())
+ I->replaceAllUsesWith(UndefValue::get(I->getType()));
+ I->eraseFromParent();
+ }
- if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) {
- SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
- ToBeDeletedBBs.reserve(NumDeadBlocks);
- ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end());
- DeleteDeadBlocks(ToBeDeletedBBs);
- }
+ if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) {
+ SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
+ ToBeDeletedBBs.reserve(NumDeadBlocks);
+ ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end());
+ DeleteDeadBlocks(ToBeDeletedBBs);
+ STATS_DECLTRACK(AAIsDead, BasicBlock,
+ "Number of dead basic blocks deleted.");
+ }
+
+ STATS_DECL(AAIsDead, Function, "Number of dead functions deleted.");
+ for (Function *Fn : ToBeDeletedFunctions) {
+ Fn->replaceAllUsesWith(UndefValue::get(Fn->getType()));
+ Fn->eraseFromParent();
+ STATS_TRACK(AAIsDead, Function);
+ }
+
+ // Identify dead internal functions and delete them. This happens outside
+ // the other fixpoint analysis as we might treat potentially dead functions
+ // as live to lower the number of iterations. If they happen to be dead, the
+ // below fixpoint loop will identify and eliminate them.
+ SmallVector<Function *, 8> InternalFns;
+ for (Function &F : M)
+ if (F.hasInternalLinkage())
+ InternalFns.push_back(&F);
+
+ bool FoundDeadFn = true;
+ while (FoundDeadFn) {
+ FoundDeadFn = false;
+ for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) {
+ Function *F = InternalFns[u];
+ if (!F)
+ continue;
+
+ const auto *LivenessAA =
+ lookupAAFor<AAIsDead>(IRPosition::function(*F));
+ if (LivenessAA &&
+ !checkForAllCallSites([](CallSite CS) { return false; },
+ *LivenessAA, true))
+ continue;
- for (Function *Fn : ToBeDeletedFunctions) {
- Fn->replaceAllUsesWith(UndefValue::get(Fn->getType()));
- Fn->eraseFromParent();
+ STATS_TRACK(AAIsDead, Function);
+ F->replaceAllUsesWith(UndefValue::get(F->getType()));
+ F->eraseFromParent();
+ InternalFns[u] = nullptr;
+ FoundDeadFn = true;
+ }
+ }
}
if (VerifyMaxFixpointIterations &&
}
void Attributor::identifyDefaultAbstractAttributes(Function &F) {
+ if (!VisitedFunctions.insert(&F).second)
+ return;
IRPosition FPos = IRPosition::function(F);
F.hasFnAttribute(Attribute::OptimizeNone))
continue;
+ // We look at internal functions only on-demand but if any use is not a
+ // direct call, we have to do it eagerly.
+ if (F.hasInternalLinkage()) {
+ if (llvm::all_of(F.uses(), [](const Use &U) {
+ return ImmutableCallSite(U.getUser()) &&
+ ImmutableCallSite(U.getUser()).isCallee(&U);
+ }))
+ continue;
+ }
+
// Populate the Attributor with abstract attribute opportunities in the
// function and the information cache with IR information.
A.identifyDefaultAbstractAttributes(F);
}
- return A.run() == ChangeStatus::CHANGED;
+ return A.run(M) == ChangeStatus::CHANGED;
}
PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
-; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 -S < %s | FileCheck %s
+; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=11 -S < %s | FileCheck %s
declare void @no_return_call() nofree noreturn nounwind readnone
; This internal function has no live call sites, so all its BBs are considered dead,
; and nothing should be deduced for it.
-; FIXME-NOT: define internal i32 @dead_internal_func(i32 %0)
+; CHECK-NOT: define internal i32 @dead_internal_func(i32 %0)
define internal i32 @dead_internal_func(i32 %0) {
%2 = icmp slt i32 %0, 1
br i1 %2, label %3, label %5
unreachable
}
+define linkonce_odr void @non_exact1() {
+ call void @non_dead_a0()
+ call void @non_dead_a1()
+ call void @non_dead_a2()
+ call void @non_dead_a3()
+ call void @non_dead_a4()
+ call void @non_dead_a5()
+ call void @non_dead_a6()
+ call void @non_dead_a7()
+ call void @non_dead_a8()
+ call void @non_dead_a9()
+ call void @non_dead_a10()
+ call void @non_dead_a11()
+ call void @non_dead_a12()
+ call void @non_dead_a13()
+ call void @non_dead_a14()
+ call void @non_dead_a15()
+ call void @middle()
+ ret void
+}
+define internal void @middle() {
+bb0:
+ call void @non_dead_b0()
+ call void @non_dead_b1()
+ call void @non_dead_b2()
+ call void @non_dead_b3()
+br label %bb1
+bb1:
+ call void @non_dead_b4()
+ call void @non_dead_b5()
+ call void @non_dead_b6()
+ call void @non_dead_b7()
+br label %bb2
+bb2:
+ call void @non_dead_b8()
+ call void @non_dead_b9()
+ call void @non_dead_b10()
+ call void @non_dead_b11()
+br label %bb3
+bb3:
+ call void @non_dead_b12()
+ call void @non_dead_b13()
+ call void @non_dead_b14()
+ call void @non_dead_b15()
+br label %bb4
+bb4:
+ call void @non_exact2()
+ ret void
+}
+define linkonce_odr void @non_exact2() {
+ call void @non_dead_c0()
+ call void @non_dead_c1()
+ call void @non_dead_c2()
+ call void @non_dead_c3()
+ call void @non_dead_c4()
+ call void @non_dead_c5()
+ call void @non_dead_c6()
+ call void @non_dead_c7()
+ call void @non_dead_c8()
+ call void @non_dead_c9()
+ call void @non_dead_c10()
+ call void @non_dead_c11()
+ call void @non_dead_c12()
+ call void @non_dead_c13()
+ call void @non_dead_c14()
+ call void @non_dead_c15()
+ call void @non_exact3()
+ ret void
+}
+define linkonce_odr void @non_exact3() {
+ call void @non_dead_d0()
+ call void @non_dead_d1()
+ call void @non_dead_d2()
+ call void @non_dead_d3()
+ call void @non_dead_d4()
+ call void @non_dead_d5()
+ call void @non_dead_d6()
+ call void @non_dead_d7()
+ call void @non_dead_d8()
+ call void @non_dead_d9()
+ call void @non_dead_d10()
+ call void @non_dead_d11()
+ call void @non_dead_d12()
+ call void @non_dead_d13()
+ call void @non_dead_d14()
+ call void @non_dead_d15()
+ %nr = call i32 @foo_noreturn()
+ call void @dead_e1()
+ ret void
+}
+; CHECK: define linkonce_odr void @non_exact3() {
+; CHECK-NEXT: call void @non_dead_d0()
+; CHECK-NEXT: call void @non_dead_d1()
+; CHECK-NEXT: call void @non_dead_d2()
+; CHECK-NEXT: call void @non_dead_d3()
+; CHECK-NEXT: call void @non_dead_d4()
+; CHECK-NEXT: call void @non_dead_d5()
+; CHECK-NEXT: call void @non_dead_d6()
+; CHECK-NEXT: call void @non_dead_d7()
+; CHECK-NEXT: call void @non_dead_d8()
+; CHECK-NEXT: call void @non_dead_d9()
+; CHECK-NEXT: call void @non_dead_d10()
+; CHECK-NEXT: call void @non_dead_d11()
+; CHECK-NEXT: call void @non_dead_d12()
+; CHECK-NEXT: call void @non_dead_d13()
+; CHECK-NEXT: call void @non_dead_d14()
+; CHECK-NEXT: call void @non_dead_d15()
+; CHECK-NEXT: %nr = call i32 @foo_noreturn()
+; CHECK-NEXT: unreachable
+; CHECK-NEXT: }
+
+define internal void @non_dead_a0() { ret void }
+define internal void @non_dead_a1() { ret void }
+define internal void @non_dead_a2() { ret void }
+define internal void @non_dead_a3() { ret void }
+define internal void @non_dead_a4() { ret void }
+define internal void @non_dead_a5() { ret void }
+define internal void @non_dead_a6() { ret void }
+define internal void @non_dead_a7() { ret void }
+define internal void @non_dead_a8() { ret void }
+define internal void @non_dead_a9() { ret void }
+define internal void @non_dead_a10() { ret void }
+define internal void @non_dead_a11() { ret void }
+define internal void @non_dead_a12() { ret void }
+define internal void @non_dead_a13() { ret void }
+define internal void @non_dead_a14() { ret void }
+define internal void @non_dead_a15() { ret void }
+define internal void @non_dead_b0() { ret void }
+define internal void @non_dead_b1() { ret void }
+define internal void @non_dead_b2() { ret void }
+define internal void @non_dead_b3() { ret void }
+define internal void @non_dead_b4() { ret void }
+define internal void @non_dead_b5() { ret void }
+define internal void @non_dead_b6() { ret void }
+define internal void @non_dead_b7() { ret void }
+define internal void @non_dead_b8() { ret void }
+define internal void @non_dead_b9() { ret void }
+define internal void @non_dead_b10() { ret void }
+define internal void @non_dead_b11() { ret void }
+define internal void @non_dead_b12() { ret void }
+define internal void @non_dead_b13() { ret void }
+define internal void @non_dead_b14() { ret void }
+define internal void @non_dead_b15() { ret void }
+define internal void @non_dead_c0() { ret void }
+define internal void @non_dead_c1() { ret void }
+define internal void @non_dead_c2() { ret void }
+define internal void @non_dead_c3() { ret void }
+define internal void @non_dead_c4() { ret void }
+define internal void @non_dead_c5() { ret void }
+define internal void @non_dead_c6() { ret void }
+define internal void @non_dead_c7() { ret void }
+define internal void @non_dead_c8() { ret void }
+define internal void @non_dead_c9() { ret void }
+define internal void @non_dead_c10() { ret void }
+define internal void @non_dead_c11() { ret void }
+define internal void @non_dead_c12() { ret void }
+define internal void @non_dead_c13() { ret void }
+define internal void @non_dead_c14() { ret void }
+define internal void @non_dead_c15() { ret void }
+define internal void @non_dead_d0() { ret void }
+define internal void @non_dead_d1() { ret void }
+define internal void @non_dead_d2() { ret void }
+define internal void @non_dead_d3() { ret void }
+define internal void @non_dead_d4() { ret void }
+define internal void @non_dead_d5() { ret void }
+define internal void @non_dead_d6() { ret void }
+define internal void @non_dead_d7() { ret void }
+define internal void @non_dead_d8() { ret void }
+define internal void @non_dead_d9() { ret void }
+define internal void @non_dead_d10() { ret void }
+define internal void @non_dead_d11() { ret void }
+define internal void @non_dead_d12() { ret void }
+define internal void @non_dead_d13() { ret void }
+define internal void @non_dead_d14() { ret void }
+define internal void @non_dead_d15() { ret void }
+define internal void @dead_e0() { call void @dead_e1() ret void }
+define internal void @dead_e1() { call void @dead_e2() ret void }
+define internal void @dead_e2() { ret void }
+
+; CHECK: define internal void @non_dead_a0()
+; CHECK: define internal void @non_dead_a1()
+; CHECK: define internal void @non_dead_a2()
+; CHECK: define internal void @non_dead_a3()
+; CHECK: define internal void @non_dead_a4()
+; CHECK: define internal void @non_dead_a5()
+; CHECK: define internal void @non_dead_a6()
+; CHECK: define internal void @non_dead_a7()
+; CHECK: define internal void @non_dead_a8()
+; CHECK: define internal void @non_dead_a9()
+; CHECK: define internal void @non_dead_a10()
+; CHECK: define internal void @non_dead_a11()
+; CHECK: define internal void @non_dead_a12()
+; CHECK: define internal void @non_dead_a13()
+; CHECK: define internal void @non_dead_a14()
+; CHECK: define internal void @non_dead_a15()
+; CHECK: define internal void @non_dead_b0()
+; CHECK: define internal void @non_dead_b1()
+; CHECK: define internal void @non_dead_b2()
+; CHECK: define internal void @non_dead_b3()
+; CHECK: define internal void @non_dead_b4()
+; CHECK: define internal void @non_dead_b5()
+; CHECK: define internal void @non_dead_b6()
+; CHECK: define internal void @non_dead_b7()
+; CHECK: define internal void @non_dead_b8()
+; CHECK: define internal void @non_dead_b9()
+; CHECK: define internal void @non_dead_b10()
+; CHECK: define internal void @non_dead_b11()
+; CHECK: define internal void @non_dead_b12()
+; CHECK: define internal void @non_dead_b13()
+; CHECK: define internal void @non_dead_b14()
+; CHECK: define internal void @non_dead_b15()
+; CHECK: define internal void @non_dead_c0()
+; CHECK: define internal void @non_dead_c1()
+; CHECK: define internal void @non_dead_c2()
+; CHECK: define internal void @non_dead_c3()
+; CHECK: define internal void @non_dead_c4()
+; CHECK: define internal void @non_dead_c5()
+; CHECK: define internal void @non_dead_c6()
+; CHECK: define internal void @non_dead_c7()
+; CHECK: define internal void @non_dead_c8()
+; CHECK: define internal void @non_dead_c9()
+; CHECK: define internal void @non_dead_c10()
+; CHECK: define internal void @non_dead_c11()
+; CHECK: define internal void @non_dead_c12()
+; CHECK: define internal void @non_dead_c13()
+; CHECK: define internal void @non_dead_c14()
+; CHECK: define internal void @non_dead_c15()
+; CHECK: define internal void @non_dead_d0()
+; CHECK: define internal void @non_dead_d1()
+; CHECK: define internal void @non_dead_d2()
+; CHECK: define internal void @non_dead_d3()
+; CHECK: define internal void @non_dead_d4()
+; CHECK: define internal void @non_dead_d5()
+; CHECK: define internal void @non_dead_d6()
+; CHECK: define internal void @non_dead_d7()
+; CHECK: define internal void @non_dead_d8()
+; CHECK: define internal void @non_dead_d9()
+; CHECK: define internal void @non_dead_d10()
+; CHECK: define internal void @non_dead_d11()
+; CHECK: define internal void @non_dead_d12()
+; CHECK: define internal void @non_dead_d13()
+; CHECK: define internal void @non_dead_d14()
+; Verify we actually deduce information for these functions.
+; CHECK: Function Attrs: nofree nosync nounwind willreturn
+; CHECK-NEXT: define internal void @non_dead_d15()
+; CHECK-NOT: define internal void @dead_e