Index: llvm/trunk/include/llvm/Analysis/CGSCCPassManager.h =================================================================== --- llvm/trunk/include/llvm/Analysis/CGSCCPassManager.h +++ llvm/trunk/include/llvm/Analysis/CGSCCPassManager.h @@ -291,6 +291,21 @@ /// post-order walk. LazyCallGraph::SCC *UpdatedC; + /// Preserved analyses across SCCs. + /// + /// We specifically want to allow CGSCC passes to mutate ancestor IR + /// (changing both the CG structure and the function IR itself). However, + /// this means we need to take special care to correctly mark what analyses + /// are preserved *across* SCCs. We have to track this out-of-band here + /// because within the main `PassManeger` infrastructure we need to mark + /// everything within an SCC as preserved in order to avoid repeatedly + /// invalidating the same analyses as we unnest pass managers and adaptors. + /// So we track the cross-SCC version of the preserved analyses here from any + /// code that does direct invalidation of SCC analyses, and then use it + /// whenever we move forward in the post-order walk of SCCs before running + /// passes over the new SCC. + PreservedAnalyses CrossSCCPA; + /// A hacky area where the inliner can retain history about inlining /// decisions that mutated the call graph's SCC structure in order to avoid /// infinite inlining. See the comments in the inliner's CG update logic. @@ -338,175 +353,7 @@ } /// Runs the CGSCC pass across every SCC in the module. - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { - // Setup the CGSCC analysis manager from its proxy. - CGSCCAnalysisManager &CGAM = - AM.getResult(M).getManager(); - - // Get the call graph for this module. - LazyCallGraph &CG = AM.getResult(M); - - // We keep worklists to allow us to push more work onto the pass manager as - // the passes are run. - SmallPriorityWorklist RCWorklist; - SmallPriorityWorklist CWorklist; - - // Keep sets for invalidated SCCs and RefSCCs that should be skipped when - // iterating off the worklists. - SmallPtrSet InvalidRefSCCSet; - SmallPtrSet InvalidSCCSet; - - SmallDenseSet, 4> - InlinedInternalEdges; - - CGSCCUpdateResult UR = {RCWorklist, CWorklist, InvalidRefSCCSet, - InvalidSCCSet, nullptr, nullptr, - InlinedInternalEdges}; - - // Request PassInstrumentation from analysis manager, will use it to run - // instrumenting callbacks for the passes later. - PassInstrumentation PI = AM.getResult(M); - - PreservedAnalyses PA = PreservedAnalyses::all(); - CG.buildRefSCCs(); - for (auto RCI = CG.postorder_ref_scc_begin(), - RCE = CG.postorder_ref_scc_end(); - RCI != RCE;) { - assert(RCWorklist.empty() && - "Should always start with an empty RefSCC worklist"); - // The postorder_ref_sccs range we are walking is lazily constructed, so - // we only push the first one onto the worklist. The worklist allows us - // to capture *new* RefSCCs created during transformations. - // - // We really want to form RefSCCs lazily because that makes them cheaper - // to update as the program is simplified and allows us to have greater - // cache locality as forming a RefSCC touches all the parts of all the - // functions within that RefSCC. - // - // We also eagerly increment the iterator to the next position because - // the CGSCC passes below may delete the current RefSCC. - RCWorklist.insert(&*RCI++); - - do { - LazyCallGraph::RefSCC *RC = RCWorklist.pop_back_val(); - if (InvalidRefSCCSet.count(RC)) { - LLVM_DEBUG(dbgs() << "Skipping an invalid RefSCC...\n"); - continue; - } - - assert(CWorklist.empty() && - "Should always start with an empty SCC worklist"); - - LLVM_DEBUG(dbgs() << "Running an SCC pass across the RefSCC: " << *RC - << "\n"); - - // Push the initial SCCs in reverse post-order as we'll pop off the - // back and so see this in post-order. - for (LazyCallGraph::SCC &C : llvm::reverse(*RC)) - CWorklist.insert(&C); - - do { - LazyCallGraph::SCC *C = CWorklist.pop_back_val(); - // Due to call graph mutations, we may have invalid SCCs or SCCs from - // other RefSCCs in the worklist. The invalid ones are dead and the - // other RefSCCs should be queued above, so we just need to skip both - // scenarios here. - if (InvalidSCCSet.count(C)) { - LLVM_DEBUG(dbgs() << "Skipping an invalid SCC...\n"); - continue; - } - if (&C->getOuterRefSCC() != RC) { - LLVM_DEBUG(dbgs() - << "Skipping an SCC that is now part of some other " - "RefSCC...\n"); - continue; - } - - do { - // Check that we didn't miss any update scenario. - assert(!InvalidSCCSet.count(C) && "Processing an invalid SCC!"); - assert(C->begin() != C->end() && "Cannot have an empty SCC!"); - assert(&C->getOuterRefSCC() == RC && - "Processing an SCC in a different RefSCC!"); - - UR.UpdatedRC = nullptr; - UR.UpdatedC = nullptr; - - // Check the PassInstrumentation's BeforePass callbacks before - // running the pass, skip its execution completely if asked to - // (callback returns false). - if (!PI.runBeforePass(Pass, *C)) - continue; - - PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR); - - if (UR.InvalidatedSCCs.count(C)) - PI.runAfterPassInvalidated(Pass); - else - PI.runAfterPass(Pass, *C); - - // Update the SCC and RefSCC if necessary. - C = UR.UpdatedC ? UR.UpdatedC : C; - RC = UR.UpdatedRC ? UR.UpdatedRC : RC; - - // If the CGSCC pass wasn't able to provide a valid updated SCC, - // the current SCC may simply need to be skipped if invalid. - if (UR.InvalidatedSCCs.count(C)) { - LLVM_DEBUG(dbgs() - << "Skipping invalidated root or island SCC!\n"); - break; - } - // Check that we didn't miss any update scenario. - assert(C->begin() != C->end() && "Cannot have an empty SCC!"); - - // We handle invalidating the CGSCC analysis manager's information - // for the (potentially updated) SCC here. Note that any other SCCs - // whose structure has changed should have been invalidated by - // whatever was updating the call graph. This SCC gets invalidated - // late as it contains the nodes that were actively being - // processed. - CGAM.invalidate(*C, PassPA); - - // Then intersect the preserved set so that invalidation of module - // analyses will eventually occur when the module pass completes. - PA.intersect(std::move(PassPA)); - - // The pass may have restructured the call graph and refined the - // current SCC and/or RefSCC. We need to update our current SCC and - // RefSCC pointers to follow these. Also, when the current SCC is - // refined, re-run the SCC pass over the newly refined SCC in order - // to observe the most precise SCC model available. This inherently - // cannot cycle excessively as it only happens when we split SCCs - // apart, at most converging on a DAG of single nodes. - // FIXME: If we ever start having RefSCC passes, we'll want to - // iterate there too. - if (UR.UpdatedC) - LLVM_DEBUG(dbgs() - << "Re-running SCC passes after a refinement of the " - "current SCC: " - << *UR.UpdatedC << "\n"); - - // Note that both `C` and `RC` may at this point refer to deleted, - // invalid SCC and RefSCCs respectively. But we will short circuit - // the processing when we check them in the loop above. - } while (UR.UpdatedC); - } while (!CWorklist.empty()); - - // We only need to keep internal inlined edge information within - // a RefSCC, clear it to save on space and let the next time we visit - // any of these functions have a fresh start. - InlinedInternalEdges.clear(); - } while (!RCWorklist.empty()); - } - - // By definition we preserve the call garph, all SCC analyses, and the - // analysis proxies by handling them above and in any nested pass managers. - PA.preserveSet>(); - PA.preserve(); - PA.preserve(); - PA.preserve(); - return PA; - } + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); private: CGSCCPassT Pass; @@ -872,6 +719,210 @@ return DevirtSCCRepeatedPass(std::move(Pass), MaxIterations); } +// Out-of-line implementation details for templates below this point. + +template +PreservedAnalyses +ModuleToPostOrderCGSCCPassAdaptor::run(Module &M, + ModuleAnalysisManager &AM) { + // Setup the CGSCC analysis manager from its proxy. + CGSCCAnalysisManager &CGAM = + AM.getResult(M).getManager(); + + // Get the call graph for this module. + LazyCallGraph &CG = AM.getResult(M); + + // We keep worklists to allow us to push more work onto the pass manager as + // the passes are run. + SmallPriorityWorklist RCWorklist; + SmallPriorityWorklist CWorklist; + + // Keep sets for invalidated SCCs and RefSCCs that should be skipped when + // iterating off the worklists. + SmallPtrSet InvalidRefSCCSet; + SmallPtrSet InvalidSCCSet; + + SmallDenseSet, 4> + InlinedInternalEdges; + + CGSCCUpdateResult UR = { + RCWorklist, CWorklist, InvalidRefSCCSet, InvalidSCCSet, + nullptr, nullptr, PreservedAnalyses::all(), InlinedInternalEdges}; + + // Request PassInstrumentation from analysis manager, will use it to run + // instrumenting callbacks for the passes later. + PassInstrumentation PI = AM.getResult(M); + + PreservedAnalyses PA = PreservedAnalyses::all(); + CG.buildRefSCCs(); + for (auto RCI = CG.postorder_ref_scc_begin(), + RCE = CG.postorder_ref_scc_end(); + RCI != RCE;) { + assert(RCWorklist.empty() && + "Should always start with an empty RefSCC worklist"); + // The postorder_ref_sccs range we are walking is lazily constructed, so + // we only push the first one onto the worklist. The worklist allows us + // to capture *new* RefSCCs created during transformations. + // + // We really want to form RefSCCs lazily because that makes them cheaper + // to update as the program is simplified and allows us to have greater + // cache locality as forming a RefSCC touches all the parts of all the + // functions within that RefSCC. + // + // We also eagerly increment the iterator to the next position because + // the CGSCC passes below may delete the current RefSCC. + RCWorklist.insert(&*RCI++); + + do { + LazyCallGraph::RefSCC *RC = RCWorklist.pop_back_val(); + if (InvalidRefSCCSet.count(RC)) { + LLVM_DEBUG(dbgs() << "Skipping an invalid RefSCC...\n"); + continue; + } + + assert(CWorklist.empty() && + "Should always start with an empty SCC worklist"); + + LLVM_DEBUG(dbgs() << "Running an SCC pass across the RefSCC: " << *RC + << "\n"); + + // Push the initial SCCs in reverse post-order as we'll pop off the + // back and so see this in post-order. + for (LazyCallGraph::SCC &C : llvm::reverse(*RC)) + CWorklist.insert(&C); + + do { + LazyCallGraph::SCC *C = CWorklist.pop_back_val(); + // Due to call graph mutations, we may have invalid SCCs or SCCs from + // other RefSCCs in the worklist. The invalid ones are dead and the + // other RefSCCs should be queued above, so we just need to skip both + // scenarios here. + if (InvalidSCCSet.count(C)) { + LLVM_DEBUG(dbgs() << "Skipping an invalid SCC...\n"); + continue; + } + if (&C->getOuterRefSCC() != RC) { + LLVM_DEBUG(dbgs() << "Skipping an SCC that is now part of some other " + "RefSCC...\n"); + continue; + } + + // Ensure we can proxy analysis updates from from the CGSCC analysis + // manager into the Function analysis manager by getting a proxy here. + // FIXME: This seems like a bit of a hack. We should find a cleaner + // or more costructive way to ensure this happens. + (void)CGAM.getResult(*C, CG); + + // Each time we visit a new SCC pulled off the worklist, + // a transformation of a child SCC may have also modified this parent + // and invalidated analyses. So we invalidate using the update record's + // cross-SCC preserved set. This preserved set is intersected by any + // CGSCC pass that handles invalidation (primarily pass managers) prior + // to marking its SCC as preserved. That lets us track everything that + // might need invalidation across SCCs without excessive invalidations + // on a single SCC. + // + // This essentially allows SCC passes to freely invalidate analyses + // of any ancestor SCC. If this becomes detrimental to successfully + // caching analyses, we could force each SCC pass to manually + // invalidate the analyses for any SCCs other than themselves which + // are mutated. However, that seems to lose the robustness of the + // pass-manager driven invalidation scheme. + // + // FIXME: This is redundant in one case -- the top of the worklist may + // *also* be the same SCC we just ran over (and invalidated for). In + // that case, we'll end up doing a redundant invalidation here as + // a consequence. + CGAM.invalidate(*C, UR.CrossSCCPA); + + do { + // Check that we didn't miss any update scenario. + assert(!InvalidSCCSet.count(C) && "Processing an invalid SCC!"); + assert(C->begin() != C->end() && "Cannot have an empty SCC!"); + assert(&C->getOuterRefSCC() == RC && + "Processing an SCC in a different RefSCC!"); + + UR.UpdatedRC = nullptr; + UR.UpdatedC = nullptr; + + // Check the PassInstrumentation's BeforePass callbacks before + // running the pass, skip its execution completely if asked to + // (callback returns false). + if (!PI.runBeforePass(Pass, *C)) + continue; + + PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR); + + if (UR.InvalidatedSCCs.count(C)) + PI.runAfterPassInvalidated(Pass); + else + PI.runAfterPass(Pass, *C); + + // Update the SCC and RefSCC if necessary. + C = UR.UpdatedC ? UR.UpdatedC : C; + RC = UR.UpdatedRC ? UR.UpdatedRC : RC; + + // If the CGSCC pass wasn't able to provide a valid updated SCC, + // the current SCC may simply need to be skipped if invalid. + if (UR.InvalidatedSCCs.count(C)) { + LLVM_DEBUG(dbgs() << "Skipping invalidated root or island SCC!\n"); + break; + } + // Check that we didn't miss any update scenario. + assert(C->begin() != C->end() && "Cannot have an empty SCC!"); + + // We handle invalidating the CGSCC analysis manager's information + // for the (potentially updated) SCC here. Note that any other SCCs + // whose structure has changed should have been invalidated by + // whatever was updating the call graph. This SCC gets invalidated + // late as it contains the nodes that were actively being + // processed. + CGAM.invalidate(*C, PassPA); + + // Then intersect the preserved set so that invalidation of module + // analyses will eventually occur when the module pass completes. + // Also intersect with the cross-SCC preserved set to capture any + // cross-SCC invalidation. + UR.CrossSCCPA.intersect(PassPA); + PA.intersect(std::move(PassPA)); + + // The pass may have restructured the call graph and refined the + // current SCC and/or RefSCC. We need to update our current SCC and + // RefSCC pointers to follow these. Also, when the current SCC is + // refined, re-run the SCC pass over the newly refined SCC in order + // to observe the most precise SCC model available. This inherently + // cannot cycle excessively as it only happens when we split SCCs + // apart, at most converging on a DAG of single nodes. + // FIXME: If we ever start having RefSCC passes, we'll want to + // iterate there too. + if (UR.UpdatedC) + LLVM_DEBUG(dbgs() + << "Re-running SCC passes after a refinement of the " + "current SCC: " + << *UR.UpdatedC << "\n"); + + // Note that both `C` and `RC` may at this point refer to deleted, + // invalid SCC and RefSCCs respectively. But we will short circuit + // the processing when we check them in the loop above. + } while (UR.UpdatedC); + } while (!CWorklist.empty()); + + // We only need to keep internal inlined edge information within + // a RefSCC, clear it to save on space and let the next time we visit + // any of these functions have a fresh start. + InlinedInternalEdges.clear(); + } while (!RCWorklist.empty()); + } + + // By definition we preserve the call garph, all SCC analyses, and the + // analysis proxies by handling them above and in any nested pass managers. + PA.preserveSet>(); + PA.preserve(); + PA.preserve(); + PA.preserve(); + return PA; +} + // Clear out the debug logging macro. #undef DEBUG_TYPE Index: llvm/trunk/lib/Analysis/CGSCCPassManager.cpp =================================================================== --- llvm/trunk/lib/Analysis/CGSCCPassManager.cpp +++ llvm/trunk/lib/Analysis/CGSCCPassManager.cpp @@ -110,6 +110,12 @@ // ...getContext().yield(); } + // Before we mark all of *this* SCC's analyses as preserved below, intersect + // this with the cross-SCC preserved analysis set. This is used to allow + // CGSCC passes to mutate ancestor SCCs and still trigger proper invalidation + // for them. + UR.CrossSCCPA.intersect(PA); + // Invalidation was handled after each pass in the above loop for the current // SCC. Therefore, the remaining analysis results in the AnalysisManager are // preserved. We mark this with a set so that we don't need to inspect each Index: llvm/trunk/test/Other/new-pass-manager.ll =================================================================== --- llvm/trunk/test/Other/new-pass-manager.ll +++ llvm/trunk/test/Other/new-pass-manager.ll @@ -24,7 +24,9 @@ ; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module> ; CHECK-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis ; CHECK-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis +; CHECK-CGSCC-PASS-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-CGSCC-PASS-NEXT: Running analysis: PassInstrumentationAnalysis +; CHECK-CGSCC-PASS-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}> ; CHECK-CGSCC-PASS-NEXT: Starting CGSCC pass manager run ; CHECK-CGSCC-PASS-NEXT: Running pass: NoOpCGSCCPass ; CHECK-CGSCC-PASS-NEXT: Finished CGSCC pass manager run @@ -410,7 +412,9 @@ ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module> ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis +; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: PassInstrumentationAnalysis +; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}> ; CHECK-REPEAT-CGSCC-PASS-NEXT: Starting CGSCC pass manager run ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running pass: RepeatedPass ; CHECK-REPEAT-CGSCC-PASS-NEXT: Starting CGSCC pass manager run Index: llvm/trunk/test/Other/new-pm-defaults.ll =================================================================== --- llvm/trunk/test/Other/new-pm-defaults.ll +++ llvm/trunk/test/Other/new-pm-defaults.ll @@ -117,11 +117,11 @@ ; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy ; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-O-NEXT: Running analysis: PassInstrumentationAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}> ; CHECK-O-NEXT: Starting CGSCC pass manager run. ; CHECK-O-NEXT: Running pass: InlinerPass -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph{{.*}}> -; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass ; CHECK-O-NEXT: Running pass: CGSCCToFunctionPassAdaptor<{{.*}}PassManager{{.*}}> Index: llvm/trunk/test/Other/new-pm-thinlto-defaults.ll =================================================================== --- llvm/trunk/test/Other/new-pm-thinlto-defaults.ll +++ llvm/trunk/test/Other/new-pm-thinlto-defaults.ll @@ -98,11 +98,11 @@ ; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy ; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-O-NEXT: Running analysis: PassInstrumentationAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}> ; CHECK-O-NEXT: Starting CGSCC pass manager run. ; CHECK-O-NEXT: Running pass: InlinerPass -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph{{.*}}> -; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass ; CHECK-O-NEXT: Running pass: CGSCCToFunctionPassAdaptor<{{.*}}PassManager{{.*}}> Index: llvm/trunk/test/Transforms/ArgumentPromotion/invalidation.ll =================================================================== --- llvm/trunk/test/Transforms/ArgumentPromotion/invalidation.ll +++ llvm/trunk/test/Transforms/ArgumentPromotion/invalidation.ll @@ -0,0 +1,50 @@ +; Check that when argument promotion changes a function in some parent node of +; the call graph, any analyses that happened to be cached for that function are +; actually invalidated. We are using `demanded-bits` here because when printed +; it will end up caching a value for every instruction, making it easy to +; detect the instruction-level changes that will fail here. With improper +; invalidation this will crash in the second printer as it tries to reuse +; now-invalid demanded bits. +; +; RUN: opt < %s -passes='function(print),cgscc(argpromotion,function(print))' -S | FileCheck %s + +@G = constant i32 0 + +define internal i32 @a(i32* %x) { +; CHECK-LABEL: define internal i32 @a( +; CHECK-SAME: i32 %[[V:.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 %[[V]] +; CHECK-NEXT: } +entry: + %v = load i32, i32* %x + ret i32 %v +} + +define i32 @b() { +; CHECK-LABEL: define i32 @b() +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[L:.*]] = load i32, i32* @G +; CHECK-NEXT: %[[V:.*]] = call i32 @a(i32 %[[L]]) +; CHECK-NEXT: ret i32 %[[V]] +; CHECK-NEXT: } +entry: + %v = call i32 @a(i32* @G) + ret i32 %v +} + +define i32 @c() { +; CHECK-LABEL: define i32 @c() +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[L:.*]] = load i32, i32* @G +; CHECK-NEXT: %[[V1:.*]] = call i32 @a(i32 %[[L]]) +; CHECK-NEXT: %[[V2:.*]] = call i32 @b() +; CHECK-NEXT: %[[RESULT:.*]] = add i32 %[[V1]], %[[V2]] +; CHECK-NEXT: ret i32 %[[RESULT]] +; CHECK-NEXT: } +entry: + %v1 = call i32 @a(i32* @G) + %v2 = call i32 @b() + %result = add i32 %v1, %v2 + ret i32 %result +} Index: llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll =================================================================== --- llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll +++ llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll @@ -8,7 +8,6 @@ ; ; CHECK-LABEL: Starting llvm::Module pass manager run. ; CHECK: Running pass: InlinerPass on (test1_f, test1_g, test1_h) -; CHECK: Running analysis: FunctionAnalysisManagerCGSCCProxy on (test1_f, test1_g, test1_h) ; CHECK: Running analysis: DominatorTreeAnalysis on test1_f ; CHECK: Running analysis: DominatorTreeAnalysis on test1_g ; CHECK: Invalidating all non-preserved analyses for: (test1_f) Index: llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp =================================================================== --- llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp +++ llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -1255,26 +1255,30 @@ MPM.run(*M, MAM); // We run over four SCCs the first time. But then we split an SCC into three. - // And then we merge those three back into one. - EXPECT_EQ(4 + 3 + 1, SCCAnalysisRuns); + // And then we merge those three back into one. However, this also + // invalidates all three SCCs further down in the PO walk. + EXPECT_EQ(4 + 3 + 1 + 3, SCCAnalysisRuns); // The module analysis pass should be run three times. EXPECT_EQ(3, ModuleAnalysisRuns); // We run over four SCCs the first time. Then over the two new ones. Then the // entire module is invalidated causing a full run over all seven. Then we - // fold three SCCs back to one, and then run over the whole module again. - EXPECT_EQ(4 + 2 + 7 + 1 + 4, IndirectSCCAnalysisRuns); - EXPECT_EQ(4 + 2 + 7 + 1 + 4, DoublyIndirectSCCAnalysisRuns); + // fold three SCCs back to one, re-compute for it and the two SCCs above it + // in the graph, and then run over the whole module again. + EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 4, IndirectSCCAnalysisRuns); + EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 4, DoublyIndirectSCCAnalysisRuns); // First we run over all six functions. Then we re-run it over three when we // split their SCCs. Then we re-run over the whole module. Then we re-run - // over three functions merged back into a single SCC, and then over the - // whole module again. - EXPECT_EQ(6 + 3 + 6 + 3 + 6, FunctionAnalysisRuns); + // over three functions merged back into a single SCC, then those three + // functions again, the two functions in SCCs above it in the graph, and then + // over the whole module again. + EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 6, FunctionAnalysisRuns); // Re run the function analysis over the entire module, and then re-run it // over the `(h3, h1, h2)` SCC due to invalidation. Then we re-run it over // the entire module, then the three functions merged back into a single SCC, - // and then over the whole module. - EXPECT_EQ(6 + 3 + 6 + 3 + 6, IndirectFunctionAnalysisRuns); + // those three functions again, then the two functions in SCCs above it in + // the graph, and then over the whole module. + EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 6, IndirectFunctionAnalysisRuns); } }