diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -75,6 +75,7 @@ void initializeAssumptionCacheTrackerPass(PassRegistry&); void initializeAtomicExpandPass(PassRegistry&); void initializeAttributorLegacyPassPass(PassRegistry&); +void initializeAttributorCGSCCLegacyPassPass(PassRegistry &); void initializeBDCELegacyPassPass(PassRegistry&); void initializeBarrierNoopPass(PassRegistry&); void initializeBasicAAWrapperPassPass(PassRegistry&); diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h --- a/llvm/include/llvm/LinkAllPasses.h +++ b/llvm/include/llvm/LinkAllPasses.h @@ -191,6 +191,7 @@ (void) llvm::createInstructionNamerPass(); (void) llvm::createMetaRenamerPass(); (void) llvm::createAttributorLegacyPass(); + (void)llvm::createAttributorCGSCCLegacyPass(); (void) llvm::createPostOrderFunctionAttrsLegacyPass(); (void) llvm::createReversePostOrderFunctionAttrsPass(); (void) llvm::createMergeFunctionsPass(); 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 @@ -101,7 +101,9 @@ #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/MustExecute.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/CallSite.h" @@ -532,25 +534,16 @@ struct AnalysisGetter { template typename Analysis::Result *getAnalysis(const Function &F) { - if (!MAM || !F.getParent()) + if (!FAM || !F.getParent()) return nullptr; - auto &FAM = MAM->getResult( - const_cast(*F.getParent())) - .getManager(); - return &FAM.getResult(const_cast(F)); + return &FAM->getResult(const_cast(F)); } - template - typename Analysis::Result *getAnalysis(const Module &M) { - if (!MAM) - return nullptr; - return &MAM->getResult(const_cast(M)); - } - AnalysisGetter(ModuleAnalysisManager &MAM) : MAM(&MAM) {} + AnalysisGetter(FunctionAnalysisManager &FAM) : FAM(&FAM) {} AnalysisGetter() {} private: - ModuleAnalysisManager *MAM = nullptr; + FunctionAnalysisManager *FAM = nullptr; }; /// Data structure to hold cached (LLVM-IR) information. @@ -566,20 +559,10 @@ /// reusable, it is advised to inherit from the InformationCache and cast the /// instance down in the abstract attributes. struct InformationCache { - InformationCache(const Module &M, AnalysisGetter &AG) - : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG) { - - CallGraph *CG = AG.getAnalysis(M); - if (!CG) - return; - - DenseMap SccSize; - for (scc_iterator I = scc_begin(CG); !I.isAtEnd(); ++I) { - for (CallGraphNode *Node : *I) - SccSize[Node->getFunction()] = I->size(); - } - SccSizeOpt = std::move(SccSize); - } + InformationCache(const Module &M, AnalysisGetter &AG, + SetVector *CGSCC) + : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG), + CGSCC(CGSCC) {} /// A map type from opcodes to instructions with this opcode. using OpcodeInstMapTy = DenseMap>; @@ -619,11 +602,11 @@ return AG.getAnalysis(F); } - /// Return SCC size on call graph for function \p F. + /// Return SCC size on call graph for function \p F or 0 if unknown. unsigned getSccSize(const Function &F) { - if (!SccSizeOpt.hasValue()) - return 0; - return (SccSizeOpt.getValue())[&F]; + if (CGSCC && CGSCC->count(const_cast(&F))) + return CGSCC->size(); + return 0; } /// Return datalayout used in the module. @@ -652,8 +635,8 @@ /// Getters for analysis. AnalysisGetter &AG; - /// Cache result for scc size in the call graph - Optional> SccSizeOpt; + /// The underlying CG-SCC, or null if not available. + SetVector *CGSCC; /// Give the Attributor access to the members so /// Attributor::identifyDefaultAbstractAttributes(...) can initialize them. @@ -690,15 +673,18 @@ struct Attributor { /// Constructor /// + /// \param Functions The set of functions we are deriving attributes for. /// \param InfoCache Cache to hold various information accessible for /// the abstract attributes. + /// \param CGUpdater Helper to update an underlying call graph. /// \param DepRecomputeInterval Number of iterations until the dependences /// between abstract attributes are recomputed. /// \param Whitelist If not null, a set limiting the attribute opportunities. - Attributor(InformationCache &InfoCache, unsigned DepRecomputeInterval, + Attributor(SetVector &Functions, InformationCache &InfoCache, + CallGraphUpdater &CGUpdater, unsigned DepRecomputeInterval, DenseSet *Whitelist = nullptr) - : InfoCache(InfoCache), DepRecomputeInterval(DepRecomputeInterval), - Whitelist(Whitelist) {} + : Functions(Functions), InfoCache(InfoCache), CGUpdater(CGUpdater), + DepRecomputeInterval(DepRecomputeInterval), Whitelist(Whitelist) {} ~Attributor() { DeleteContainerPointers(AllAbstractAttributes); } @@ -708,7 +694,7 @@ /// as the Attributor is not destroyed (it owns the attributes now). /// /// \Returns CHANGED if the IR was changed, otherwise UNCHANGED. - ChangeStatus run(Module &M); + ChangeStatus run(); /// Lookup an abstract attribute of type \p AAType at position \p IRP. While /// no abstract attribute is found equivalent positions are checked, see @@ -931,7 +917,8 @@ bool Invalidate = Whitelist && !Whitelist->count(&AAType::ID); if (const Function *Fn = IRP.getAnchorScope()) Invalidate |= Fn->hasFnAttribute(Attribute::Naked) || - Fn->hasFnAttribute(Attribute::OptimizeNone); + Fn->hasFnAttribute(Attribute::OptimizeNone) || + !Functions.count(const_cast(Fn)); // Bootstrap the new attribute with an initial update to propagate // information, e.g., function -> call site. If it is not on a given @@ -1007,9 +994,15 @@ QueryMapTy QueryMap; ///} + /// The set of functions we are deriving attributes for. + SetVector &Functions; + /// The information cache that holds pre-processed (LLVM-IR) information. InformationCache &InfoCache; + /// Helper to update an underlying call graph. + CallGraphUpdater &CGUpdater; + /// Set if the attribute currently updated did query a non-fix attribute. bool QueriedNonFixAA; @@ -1542,8 +1535,13 @@ struct AttributorPass : public PassInfoMixin { PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; +struct AttributorCGSCCPass : public PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR); +}; Pass *createAttributorLegacyPass(); +Pass *createAttributorCGSCCLegacyPass(); /// ---------------------------------------------------------------------------- /// Abstract Attribute Classes diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp --- a/llvm/lib/LTO/LTOCodeGenerator.cpp +++ b/llvm/lib/LTO/LTOCodeGenerator.cpp @@ -137,6 +137,7 @@ initializeJumpThreadingPass(R); initializeSROALegacyPassPass(R); initializeAttributorLegacyPassPass(R); + initializeAttributorCGSCCLegacyPassPass(R); initializePostOrderFunctionAttrsLegacyPassPass(R); initializeReversePostOrderFunctionAttrsLegacyPassPass(R); initializeGlobalsAAWrapperPassPass(R); diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -726,6 +726,7 @@ MPM.addPass(PGOIndirectCallPromotion(Phase == ThinLTOPhase::PostLink, true /* SamplePGO */)); } + MPM.addPass(AttributorPass()); // Interprocedural constant propagation now that basic cleanup has occurred // and prior to optimizing globals. @@ -808,6 +809,8 @@ IP.HotCallSiteThreshold = 0; MainCGPipeline.addPass(InlinerPass(IP)); + MainCGPipeline.addPass(AttributorCGSCCPass()); + // Now deduce any function attributes based in the current code. MainCGPipeline.addPass(PostOrderFunctionAttrsPass()); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -107,6 +107,7 @@ CGSCC_PASS("argpromotion", ArgumentPromotionPass()) CGSCC_PASS("invalidate", InvalidateAllAnalysesPass()) CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass()) +CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass()) CGSCC_PASS("inline", InlinerPass()) CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass()) #undef CGSCC_PASS 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 @@ -20,6 +20,8 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/GlobalsModRef.h" @@ -35,6 +37,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/CallGraphUpdater.h" #include "llvm/Transforms/Utils/Local.h" #include @@ -153,6 +156,17 @@ } ///} +static void replaceAllUsesWith(Value &Old, Value &New) { + if (!isa(Old)) + return Old.replaceAllUsesWith(&New); + SmallVector Uses; + for (Use &U : Old.uses()) + if (isa(U.getUser())) + Uses.push_back(&U); + for (Use *U : Uses) + U->set(&New); +} + /// 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 @@ -983,7 +997,7 @@ auto ReplaceCallSiteUsersWith = [](CallBase &CB, Constant &C) { if (CB.getNumUses() == 0 || CB.isMustTailCall()) return ChangeStatus::UNCHANGED; - CB.replaceAllUsesWith(&C); + replaceAllUsesWith(CB, C); return ChangeStatus::CHANGED; }; @@ -2510,7 +2524,7 @@ CallInst *CI = createCallMatchingInvoke(II); CI->insertBefore(II); CI->takeName(II); - II->replaceAllUsesWith(CI); + replaceAllUsesWith(*II, *CI); // If this is a nounwind + mayreturn invoke we only remove the unwind edge. // This is done by moving the invoke into a new and dead block and connecting @@ -3790,7 +3804,7 @@ if (!V.user_empty() && &V != C && V.getType() == C->getType()) { LLVM_DEBUG(dbgs() << "[Attributor][ValueSimplify] " << V << " -> " << *C << "\n"); - V.replaceAllUsesWith(C); + replaceAllUsesWith(V, *C); Changed = ChangeStatus::CHANGED; } } @@ -4030,7 +4044,7 @@ AI = new BitCastInst(AI, MallocCall->getType(), "malloc_bc", AI->getNextNode()); - MallocCall->replaceAllUsesWith(AI); + replaceAllUsesWith(*MallocCall, *AI); if (auto *II = dyn_cast(MallocCall)) { auto *NBB = II->getNormalDest(); @@ -4967,7 +4981,7 @@ return true; } -ChangeStatus Attributor::run(Module &M) { +ChangeStatus Attributor::run() { LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized " << AllAbstractAttributes.size() << " abstract attributes.\n"); @@ -5156,6 +5170,10 @@ NumFinalAAs == AllAbstractAttributes.size() && "Expected the final number of abstract attributes to remain unchanged!"); + // Set of functions for which we modified the content such that it might + // impact the call graph. + SmallPtrSet CGModifiedFunctions; + // Delete stuff at the end to avoid invalid references and a nice order. { LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least " @@ -5175,11 +5193,12 @@ LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser() << " instead of " << *OldV << "\n"); U->set(NewV); - if (Instruction *I = dyn_cast(OldV)) + if (Instruction *I = dyn_cast(OldV)) { + CGModifiedFunctions.insert(I->getFunction()); if (!isa(I) && !ToBeDeletedInsts.count(I) && - isInstructionTriviallyDead(I)) { + isInstructionTriviallyDead(I)) DeadInsts.push_back(I); - } + } if (isa(NewV) && isa(U->getUser())) { Instruction *UserI = cast(U->getUser()); if (isa(NewV)) { @@ -5189,12 +5208,17 @@ } } } - for (Instruction *I : UnreachablesToInsert) + for (Instruction *I : UnreachablesToInsert) { + CGModifiedFunctions.insert(I->getFunction()); changeToUnreachable(I, /* UseLLVMTrap */ false); - for (Instruction *I : TerminatorsToFold) + } + for (Instruction *I : TerminatorsToFold) { + CGModifiedFunctions.insert(I->getFunction()); ConstantFoldTerminator(I->getParent()); + } for (Instruction *I : ToBeDeletedInsts) { + CGModifiedFunctions.insert(I->getFunction()); I->replaceAllUsesWith(UndefValue::get(I->getType())); if (!isa(I) && isInstructionTriviallyDead(I)) DeadInsts.push_back(I); @@ -5207,7 +5231,10 @@ if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) { SmallVector ToBeDeletedBBs; ToBeDeletedBBs.reserve(NumDeadBlocks); - ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end()); + for (BasicBlock *BB : ToBeDeletedBlocks) { + CGModifiedFunctions.insert(BB->getParent()); + ToBeDeletedBBs.push_back(BB); + } // Actually we do not delete the blocks but squash them into a single // unreachable but untangling branches that jump here is something we need // to do in a more generic way. @@ -5219,6 +5246,7 @@ STATS_DECL(AAIsDead, Function, "Number of dead functions deleted."); for (Function *Fn : ToBeDeletedFunctions) { Fn->replaceAllUsesWith(UndefValue::get(Fn->getType())); + CGUpdater.removeFunction(*Fn); Fn->eraseFromParent(); STATS_TRACK(AAIsDead, Function); } @@ -5228,9 +5256,9 @@ // as live to lower the number of iterations. If they happen to be dead, the // below fixpoint loop will identify and eliminate them. SmallVector InternalFns; - for (Function &F : M) - if (F.hasLocalLinkage()) - InternalFns.push_back(&F); + for (Function *F : Functions) + if (F->hasLocalLinkage()) + InternalFns.push_back(F); bool FoundDeadFn = true; while (FoundDeadFn) { @@ -5248,6 +5276,7 @@ ToBeDeletedFunctions.insert(F); F->deleteBody(); F->replaceAllUsesWith(UndefValue::get(F->getType())); + CGUpdater.removeFunction(*F); F->eraseFromParent(); InternalFns[u] = nullptr; FoundDeadFn = true; @@ -5264,6 +5293,9 @@ "specified iterations!"); } + for (Function *Fn : CGModifiedFunctions) + CGUpdater.reanalyzeFunction(*Fn); + return ManifestChange; } @@ -5559,48 +5591,86 @@ /// Pass (Manager) Boilerplate /// ---------------------------------------------------------------------------- -static bool runAttributorOnModule(Module &M, AnalysisGetter &AG) { - if (DisableAttributor) +static bool runAttributorOnFunctions(InformationCache &InfoCache, + SetVector &Functions, + AnalysisGetter &AG, + CallGraphUpdater &CGUpdater) { + if (DisableAttributor || Functions.empty()) return false; - LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << M.size() + LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << Functions.size() << " functions.\n"); // Create an Attributor and initially empty information cache that is filled // while we identify default attribute opportunities. - InformationCache InfoCache(M, AG); - Attributor A(InfoCache, DepRecInterval); + Attributor A(Functions, InfoCache, CGUpdater, DepRecInterval); - for (Function &F : M) - A.initializeInformationCache(F); + for (Function *F : Functions) + A.initializeInformationCache(*F); - for (Function &F : M) { - if (F.hasExactDefinition()) + for (Function *F : Functions) { + if (F->hasExactDefinition()) NumFnWithExactDefinition++; else NumFnWithoutExactDefinition++; // 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.hasLocalLinkage()) { - if (llvm::all_of(F.uses(), [](const Use &U) { - return ImmutableCallSite(U.getUser()) && - ImmutableCallSite(U.getUser()).isCallee(&U); + // direct call or outside the current set of analyzed functions, we have to + // do it eagerly. + if (F->hasLocalLinkage()) { + if (llvm::all_of(F->uses(), [&Functions](const Use &U) { + ImmutableCallSite ICS(U.getUser()); + return ICS && ICS.isCallee(&U) && + Functions.count(const_cast(ICS.getCaller())); })) continue; } // Populate the Attributor with abstract attribute opportunities in the // function and the information cache with IR information. - A.identifyDefaultAbstractAttributes(F); + A.identifyDefaultAbstractAttributes(*F); } - return A.run(M) == ChangeStatus::CHANGED; + return A.run() == ChangeStatus::CHANGED; } PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) { - AnalysisGetter AG(AM); - if (runAttributorOnModule(M, AG)) { + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + AnalysisGetter AG(FAM); + + SetVector Functions; + for (Function &F : M) + Functions.insert(&F); + + CallGraphUpdater CGUpdater; + InformationCache InfoCache(M, AG, /* CGSCC */ nullptr); + if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { + // FIXME: Think about passes we will preserve and add them here. + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +PreservedAnalyses AttributorCGSCCPass::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, + LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + FunctionAnalysisManager &FAM = + AM.getResult(C, CG).getManager(); + AnalysisGetter AG(FAM); + + SetVector Functions; + for (LazyCallGraph::Node &N : C) + Functions.insert(&N.getFunction()); + + if (Functions.empty()) + return PreservedAnalyses::all(); + + Module &M = *Functions.back()->getParent(); + CallGraphUpdater CGUpdater(CG, AM, UR); + InformationCache InfoCache(M, AG, /* CGSCC */ &Functions); + if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { // FIXME: Think about passes we will preserve and add them here. return PreservedAnalyses::none(); } @@ -5621,20 +5691,65 @@ return false; AnalysisGetter AG; - return runAttributorOnModule(M, AG); + SetVector Functions; + for (Function &F : M) + Functions.insert(&F); + + CallGraphUpdater CGUpdater; + InformationCache InfoCache(M, AG, /* CGSCC */ nullptr); + return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + // FIXME: Think about passes we will preserve and add them here. + AU.addRequired(); + } +}; + +struct AttributorCGSCCLegacyPass : public CallGraphSCCPass { + static char ID; + + AttributorCGSCCLegacyPass() : CallGraphSCCPass(ID) { + initializeAttributorCGSCCLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnSCC(CallGraphSCC &SCC) override { + if (skipSCC(SCC)) + return false; + + SetVector Functions; + for (CallGraphNode *CGN : SCC) + if (Function *Fn = CGN->getFunction()) + if (!Fn->isDeclaration()) + Functions.insert(Fn); + + if (Functions.empty()) + return false; + + AnalysisGetter AG; + CallGraph &CG = getAnalysis().getCallGraph(); + CallGraphUpdater CGUpdater(CG); + Module &M = *Functions.back()->getParent(); + InformationCache InfoCache(M, AG, /* CGSCC */ &Functions); + return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); } void getAnalysisUsage(AnalysisUsage &AU) const override { // FIXME: Think about passes we will preserve and add them here. AU.addRequired(); + CallGraphSCCPass::getAnalysisUsage(AU); } }; } // end anonymous namespace Pass *llvm::createAttributorLegacyPass() { return new AttributorLegacyPass(); } +Pass *llvm::createAttributorCGSCCLegacyPass() { + return new AttributorCGSCCLegacyPass(); +} char AttributorLegacyPass::ID = 0; +char AttributorCGSCCLegacyPass::ID = 0; const char AAReturnedValues::ID = 0; const char AANoUnwind::ID = 0; @@ -5779,3 +5894,11 @@ INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_END(AttributorLegacyPass, "attributor", "Deduce and propagate attributes", false, false) +INITIALIZE_PASS_BEGIN(AttributorCGSCCLegacyPass, "attributor-cgscc", + "Deduce and propagate attributes (CG-SCC pass)", false, + false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_END(AttributorCGSCCLegacyPass, "attributor-cgscc", + "Deduce and propagate attributes (CG-SCC pass)", false, + false) diff --git a/llvm/lib/Transforms/IPO/IPO.cpp b/llvm/lib/Transforms/IPO/IPO.cpp --- a/llvm/lib/Transforms/IPO/IPO.cpp +++ b/llvm/lib/Transforms/IPO/IPO.cpp @@ -46,6 +46,7 @@ initializeMergeFunctionsPass(Registry); initializePartialInlinerLegacyPassPass(Registry); initializeAttributorLegacyPassPass(Registry); + initializeAttributorCGSCCLegacyPassPass(Registry); initializePostOrderFunctionAttrsLegacyPassPass(Registry); initializeReversePostOrderFunctionAttrsLegacyPassPass(Registry); initializePruneEHPass(Registry); diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -509,6 +509,9 @@ // Infer attributes about declarations if possible. MPM.add(createInferFunctionAttrsLegacyPass()); + // Infer attributes on declarations, call sites, arguments, etc. + MPM.add(createAttributorLegacyPass()); + addExtensionsToPM(EP_ModuleOptimizerEarly, MPM); if (OptLevel > 2) @@ -517,9 +520,6 @@ MPM.add(createIPSCCPPass()); // IP SCCP MPM.add(createCalledValuePropagationPass()); - // Infer attributes on declarations, call sites, arguments, etc. - MPM.add(createAttributorLegacyPass()); - MPM.add(createGlobalOptimizerPass()); // Optimize out global vars // Promote any localized global vars. MPM.add(createPromoteMemoryToRegisterPass()); @@ -557,6 +557,9 @@ RunInliner = true; } + // Infer attributes on declarations, call sites, arguments, etc. for an SCC. + MPM.add(createAttributorCGSCCLegacyPass()); + MPM.add(createPostOrderFunctionAttrsLegacyPass()); if (OptLevel > 2) MPM.add(createArgumentPromotionPass()); // Scalarize uninlined fn args @@ -880,6 +883,9 @@ // CSFDO instrumentation and use pass. addPGOInstrPasses(PM, /* IsCS */ true); + // Infer attributes on declarations, call sites, arguments, etc. for an SCC. + PM.add(createAttributorCGSCCLegacyPass()); + // Optimize globals again if we ran the inliner. if (RunInliner) PM.add(createGlobalOptimizerPass()); diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -98,6 +98,7 @@ ; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass ; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass ; CHECK-O-NEXT: Finished llvm::Function pass manager run. +; CHECK-O-NEXT: Running pass: AttributorPass ; CHECK-O-NEXT: Running pass: IPSCCPPass ; CHECK-O-NEXT: Running pass: CalledValuePropagationPass ; CHECK-O-NEXT: Running pass: GlobalOptPass diff --git a/llvm/test/Other/new-pm-thinlto-defaults.ll b/llvm/test/Other/new-pm-thinlto-defaults.ll --- a/llvm/test/Other/new-pm-thinlto-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-defaults.ll @@ -79,6 +79,7 @@ ; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass ; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass ; CHECK-O-NEXT: Finished llvm::Function pass manager run. +; CHECK-O-NEXT: Running pass: AttributorPass ; CHECK-O-NEXT: Running pass: IPSCCPPass ; CHECK-O-NEXT: Running pass: CalledValuePropagationPass ; CHECK-O-NEXT: Running pass: GlobalOptPass 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,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -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/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll --- a/llvm/test/Transforms/FunctionAttrs/liveness.ll +++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s +; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s ; UTC_ARGS: --turn off ; CHECK: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -1,6 +1,6 @@ ; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s --check-prefixes=CHECK,BOTH ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-functionattrs' -S | FileCheck %s --check-prefixes=CHECK,BOTH -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,BOTH +; RUN: opt -passes=attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,BOTH ; CHECK: Function Attrs ; CHECK-SAME: norecurse nounwind readnone diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -1,5 +1,6 @@ ; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE +; RUN: opt -passes=attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @@ -11,7 +12,8 @@ ; TEST 1 (positive case) ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable ; FNATTR-NEXT: define void @only_return() -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn ; ATTRIBUTOR-NEXT: define void @only_return() define void @only_return() #0 { ret void @@ -59,7 +61,8 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable ; FNATTR-NOT: willreturn ; FNATTR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 { @@ -95,7 +98,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable ; FNATTR-NEXT: define i32 @fact_loop(i32 %0) -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32 %0) local_unnamed_addr define i32 @fact_loop(i32 %0) local_unnamed_addr #0 { %2 = icmp slt i32 %0, 1 @@ -261,7 +265,8 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable ; FNATTR-NEXT: define void @f1() -; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn +; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn ; ATTRIBUTOR-NEXT: define void @f1() define void @f1() #0 { tail call void @will_return() @@ -270,7 +275,10 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable ; FNATTR-NEXT: define void @f2() -; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn +; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable +; FIXME: Because we do not derive norecurse in the module run anymore, willreturn is missing as well. +; ATTRIBUTOR_MODULE-NOT: willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn ; ATTRIBUTOR-NEXT: define void @f2() define void @f2() #0 { tail call void @f1() @@ -284,7 +292,8 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable ; FNATTR-NOT: willreturn ; FNATTR-NEXT: define void @call_will_return_but_has_loop() -; ATTRIBUTOR: Function Attrs: noinline norecurse noreturn nounwind uwtable +; ATTRIBUTOR_MODULE: Function Attrs: noinline noreturn nounwind uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse noreturn nounwind uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define void @call_will_return_but_has_loop() define void @call_will_return_but_has_loop() #0 { @@ -337,7 +346,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable ; FNATTR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly %0) -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture nofree readonly %0) define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 { br label %3 @@ -370,7 +380,8 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable ; FNATTR-NOT: willreturn ; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture nofree readonly %2, i32 %3) local_unnamed_addr define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr #0 { @@ -408,7 +419,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable ; FNATTR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture nofree readonly %1) local_unnamed_addr define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 { @@ -439,7 +451,8 @@ ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable ; FNATTR-NEXT: define i32 @multiple_return(i32 %a) -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn ; ATTRIBUTOR-NEXT: define i32 @multiple_return(i32 %a) define i32 @multiple_return(i32 %a) #0 { %b = icmp eq i32 %a, 0 @@ -457,7 +470,8 @@ ; 15.1 (positive case) ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @unreachable_exit_positive1() -; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn +; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn ; ATTRIBUTOR-NEXT: define void @unreachable_exit_positive1() define void @unreachable_exit_positive1() #0 { tail call void @will_return() @@ -471,7 +485,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define i32 @unreachable_exit_positive2(i32 %0) -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32 %0) define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 { %2 = icmp slt i32 %0, 1 @@ -515,7 +530,8 @@ ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NOT: willreturn ; FNATTR-NEXT: define void @unreachable_exit_negative2() -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2() define void @unreachable_exit_negative2() #0 {