diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 9795922427c2..4c95392c22a3 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -297,6 +297,9 @@ struct IRPosition { if (auto *Arg = dyn_cast(&V)) if (!Arg->getParent()->isDeclaration()) return &Arg->getParent()->getEntryBlock().front(); + if (auto *F = dyn_cast(&V)) + if (!F->isDeclaration()) + return &(F->getEntryBlock().front()); return nullptr; } const Instruction *getCtxI() const { diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index bbd14d7c0c96..16894d8e8422 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -1570,13 +1570,22 @@ struct AAIsDeadImpl : public AAIsDead { void initialize(Attributor &A) override { const Function *F = getAssociatedFunction(); + + if (F->hasInternalLinkage()) + return; + if (!F || !F->hasExactDefinition()) { indicatePessimisticFixpoint(); return; } + exploreFromEntry(A, F); + } + + void exploreFromEntry(Attributor &A, const Function *F) { ToBeExploredPaths.insert(&(F->getEntryBlock().front())); AssumedLiveBlocks.insert(&(F->getEntryBlock())); + for (size_t i = 0; i < ToBeExploredPaths.size(); ++i) if (const Instruction *NextNoReturnI = findNextNoReturn(A, ToBeExploredPaths[i])) @@ -1606,7 +1615,12 @@ struct AAIsDeadImpl : public AAIsDead { "Attempted to manifest an invalid state!"); ChangeStatus HasChanged = ChangeStatus::UNCHANGED; - const Function &F = *getAssociatedFunction(); + Function &F = *getAssociatedFunction(); + + if (AssumedLiveBlocks.empty()) { + F.replaceAllUsesWith(UndefValue::get(F.getType())); + return ChangeStatus::CHANGED; + } // Flag to determine if we can change an invoke to a call assuming the // callee is nounwind. This is not possible if the personality of the @@ -1725,6 +1739,13 @@ struct AAIsDeadFunction final : public AAIsDeadImpl { /// 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) += @@ -1796,10 +1817,25 @@ const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A, } 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 NoReturnChanged; - ChangeStatus Status = ChangeStatus::UNCHANGED; for (const Instruction *I : NoReturnCalls) NoReturnChanged.push_back(I); @@ -2249,6 +2285,11 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA, if (!LivenessAA) LivenessAA = &getAAFor(AA, IRPosition::function(*CtxI->getFunction())); + + // Don't check liveness for AAIsDead. + if (&AA == LivenessAA) + return false; + if (!LivenessAA->isAssumedDead(CtxI)) return false; diff --git a/llvm/test/Transforms/FunctionAttrs/align.ll b/llvm/test/Transforms/FunctionAttrs/align.ll index 720058fa0b9b..88b3e1da2bb7 100644 --- a/llvm/test/Transforms/FunctionAttrs/align.ll +++ b/llvm/test/Transforms/FunctionAttrs/align.ll @@ -139,6 +139,7 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 { ; Better than IR information ; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 %p) define align 4 i32* @test7(i32* align 32 %p) #0 { + tail call i8* @f1(i8* align 8 dereferenceable(1) @a1) ret i32* %p } diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll index 535ba8779e3e..265ed8b2aa3d 100644 --- a/llvm/test/Transforms/FunctionAttrs/liveness.ll +++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -12,6 +12,27 @@ declare i32 @foo_noreturn() noreturn declare i32 @bar() nosync readnone +; This internal function has no live call sites, so all its BBs are considered dead, +; and nothing should be deduced for it. + +; CHECK: 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 + +;