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 @@ -607,7 +607,7 @@ /// as the Attributor is not destroyed (it owns the attributes now). /// /// \Returns CHANGED if the IR was changed, otherwise UNCHANGED. - ChangeStatus run(); + ChangeStatus run(Module &M); /// Lookup an abstract attribute of type \p AAType at position \p IRP. While /// no abstract attribute is found equivalent positions are checked, see @@ -681,6 +681,16 @@ /// various places. void identifyDefaultAbstractAttributes(Function &F); + /// Mark the internal function \p F as live. + /// + /// This will trigger the identification and initialization of attributes for + /// \p F. + void markLiveInternalFunction(const Function &F) { + assert(F.hasInternalLinkage() && + "Only internal linkage is assumed dead initially."); + identifyDefaultAbstractAttributes(const_cast(F)); + } + /// Record that \p I is deleted after information was manifested. void deleteAfterManifest(Instruction &I) { ToBeDeletedInsts.insert(&I); } @@ -837,6 +847,9 @@ /// If not null, a set limiting the attribute opportunities. const DenseSet *Whitelist; + /// A set to remember the functions we already assume to be live and visited. + DenseSet VisitedFunctions; + /// Functions, blocks, and instructions we delete after manifest is done. /// ///{ 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 @@ -139,6 +139,21 @@ } ///} +namespace { +/// A dummy abstract attribute that can be used in the query APIs. +struct AADummy : public StateWrapper, + public IRPosition { + AADummy(const IRPosition &IRP) : IRPosition(IRP) {} + IRPosition &getIRPosition() override { return *this; } + const IRPosition &getIRPosition() const override { return *this; } + const std::string getAsStr() const override { return ""; } + void trackStatistics() const override {} + ChangeStatus updateImpl(Attributor &A) override { + return ChangeStatus::CHANGED; + }; +}; +} + /// Recursively visit all values that might become \p IRP at some point. This /// will be done by looking through cast instructions, selects, phis, and calls /// with the "returned" attribute. Once we cannot look through the value any @@ -1766,17 +1781,8 @@ AAIsDeadImpl(const IRPosition &IRP) : AAIsDead(IRP) {} void initialize(Attributor &A) override { - const Function *F = getAssociatedFunction(); - - if (F->hasInternalLinkage()) - return; - - if (!F || !F->hasExactDefinition()) { - indicatePessimisticFixpoint(); - return; - } - - exploreFromEntry(A, F); + if (const Function *F = getAssociatedFunction()) + exploreFromEntry(A, F); } void exploreFromEntry(Attributor &A, const Function *F) { @@ -1946,18 +1952,6 @@ /// 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(); @@ -1993,6 +1987,12 @@ ImmutableCallSite ICS(I); if (ICS) { + // We asume ICS is live now and if it calls an internal function we will + // assume that one is live as well. + if (const Function *F = ICS.getCalledFunction()) + if (F->hasInternalLinkage()) + A.markLiveInternalFunction(*F); + const IRPosition &IPos = IRPosition::callsite_function(ICS); // Regarless of the no-return property of an invoke instruction we only // learn that the regular successor is not reachable through this @@ -2025,22 +2025,8 @@ } 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; @@ -3060,11 +3046,7 @@ 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"); @@ -3143,6 +3125,11 @@ bool FinishedAtFixpoint = Worklist.empty(); + // Identify dead internal functions and mark them to be deleted. + for (Function &F : M) + if (F.hasInternalLinkage() && !VisitedFunctions.count(&F)) + deleteAfterManifest(F); + // Reset abstract arguments not settled in a sound fixpoint by now. This // happens when we stopped the fixpoint iteration early. Note that only the // ones marked as "changed" *and* the ones transitively depending on them @@ -3217,7 +3204,7 @@ 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 " @@ -3250,11 +3237,13 @@ ToBeDeletedBBs.reserve(NumDeadBlocks); ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end()); DeleteDeadBlocks(ToBeDeletedBBs); + STATS_DECLTRACK(AAIsDead, BasicBlock, "Number of dead basic blocks."); } for (Function *Fn : ToBeDeletedFunctions) { Fn->replaceAllUsesWith(UndefValue::get(Fn->getType())); Fn->eraseFromParent(); + STATS_DECLTRACK(AAIsDead, Function, "Number of dead functions."); } if (VerifyMaxFixpointIterations && @@ -3270,6 +3259,8 @@ } void Attributor::identifyDefaultAbstractAttributes(Function &F) { + if (!VisitedFunctions.insert(&F).second) + return; IRPosition FPos = IRPosition::function(F); @@ -3490,12 +3481,23 @@ NumFnWithExactDefinition++; + // 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) { + if (ImmutableCallSite ICS = ImmutableCallSite(U.getUser())) + return ICS.isCallee(&U); + return false; + })) + 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) { 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 @@ -1,4 +1,4 @@ -; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll --- a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll @@ -1,4 +1,4 @@ -; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=9 -S < %s | FileCheck %s +; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S < %s | FileCheck %s ; ; This is an evolved example to stress test SCC parameter attribute propagation. ; The SCC in this test is made up of the following six function, three of which