diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -297,6 +297,9 @@ 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 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -1295,7 +1295,7 @@ unsigned ArgNo = getArgNo(); // Callback function - std::function CallSiteCheck = [&](CallSite CS) { + auto CallSiteCheck = [&](CallSite CS) { assert(CS && "Sanity check: Call site was not initialized properly!"); IRPosition CSArgPos = IRPosition::callsite_argument(CS, ArgNo); @@ -1589,13 +1589,22 @@ 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])) @@ -1625,7 +1634,12 @@ "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 @@ -1818,10 +1832,28 @@ } ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) { + const Function *F = getAssociatedFunction(); + ChangeStatus Status = ChangeStatus::UNCHANGED; + + if (F->hasInternalLinkage()) { + 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; + + // Only do initial explore once. + if (NoReturnCalls.empty()) { + 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); @@ -1831,18 +1863,19 @@ const Instruction *NextNoReturnI = findNextNoReturn(A, I); if (NextNoReturnI != I) { - Status = ChangeStatus::CHANGED; NoReturnCalls.remove(I); + Status = ChangeStatus::CHANGED; if (NextNoReturnI) NoReturnCalls.insert(NextNoReturnI); } // Explore new paths. while (Size != ToBeExploredPaths.size()) { - Status = ChangeStatus::CHANGED; if (const Instruction *NextNoReturnI = - findNextNoReturn(A, ToBeExploredPaths[Size++])) + findNextNoReturn(A, ToBeExploredPaths[Size++])){ NoReturnCalls.insert(NextNoReturnI); + Status = ChangeStatus::CHANGED; + } } } @@ -2115,7 +2148,7 @@ bool IsGlobal = isAssumedGlobal(); // Callback function - std::function CallSiteCheck = [&](CallSite CS) -> bool { + auto CallSiteCheck = [&](CallSite CS) { assert(CS && "Sanity check: Call site was not initialized properly!"); // Check that DereferenceableAA is AADereferenceableCallSiteArgument. @@ -2325,12 +2358,18 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA, const AAIsDead *LivenessAA) { const Instruction *CtxI = AA.getIRPosition().getCtxI(); + if (!CtxI) return false; if (!LivenessAA) LivenessAA = getAAFor(AA, IRPosition::function(*CtxI->getFunction())); + + // Don't check liveness for AAIsDead. + if (static_cast(&AA) == LivenessAA) + return false; + if (!LivenessAA || !LivenessAA->isAssumedDead(CtxI)) return false; diff --git a/llvm/test/Transforms/FunctionAttrs/align.ll b/llvm/test/Transforms/FunctionAttrs/align.ll --- a/llvm/test/Transforms/FunctionAttrs/align.ll +++ b/llvm/test/Transforms/FunctionAttrs/align.ll @@ -92,7 +92,7 @@ ; FIXME: Until we have "on-demand" attribute generation we do not determine the ; alignment for the return value here. ; define internal nonnull align 8 i8* @f1(i8* nonnull readnone align 8 %0) -; ATTRIBUTOR: define internal nonnull i8* @f1(i8* nonnull readnone align 8 %0) +; ATTRIBUTOR: define internal nonnull i8* @f1(i8* nonnull readnone %0) %2 = icmp eq i8* %0, null br i1 %2, label %3, label %5 @@ -152,8 +152,9 @@ ; TEST 7 ; 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 { +; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 %p, i8* readnone %c) +define align 4 i32* @test7(i32* align 32 %p, i8* readnone %c) #0 { + tail call i8* @f1(i8* nonnull %c) ret i32* %p } diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll --- a/llvm/test/Transforms/FunctionAttrs/liveness.ll +++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -12,6 +12,27 @@ 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 + +;