diff --git a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h --- a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h +++ b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h @@ -48,8 +48,13 @@ /// attribute. It also discovers function arguments that are not captured by /// the function and marks them with the nocapture attribute. struct PostOrderFunctionAttrsPass : PassInfoMixin { + PostOrderFunctionAttrsPass(bool SkipNonRecursive = false) + : SkipNonRecursive(SkipNonRecursive) {} PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR); + +private: + bool SkipNonRecursive; }; /// A pass to do RPO deduction and propagation of function attributes. diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp --- a/llvm/lib/Analysis/CGSCCPassManager.cpp +++ b/llvm/lib/Analysis/CGSCCPassManager.cpp @@ -545,8 +545,6 @@ // function's analyses (that's the contract of a function pass), so // directly handle the function analysis manager's invalidation here. FAM.invalidate(F, EagerlyInvalidate ? PreservedAnalyses::none() : PassPA); - if (NoRerun) - (void)FAM.getResult(F); // Then intersect the preserved set so that invalidation of module // analyses will eventually occur when the module pass completes. diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -831,8 +831,11 @@ if (AttributorRun & AttributorRunOption::CGSCC) MainCGPipeline.addPass(AttributorCGSCCPass()); - // Now deduce any function attributes based in the current code. - MainCGPipeline.addPass(PostOrderFunctionAttrsPass()); + // Deduce function attributes. We do another run of this after the function + // simplification pipeline, so this only needs to run when it could affect the + // function simplification pipeline, which is only the case with recursive + // functions. + MainCGPipeline.addPass(PostOrderFunctionAttrsPass(/*SkipNonRecursive*/ true)); // When at O3 add argument promotion to the pass pipeline. // FIXME: It isn't at all clear why this should be limited to O3. @@ -847,14 +850,24 @@ for (auto &C : CGSCCOptimizerLateEPCallbacks) C(MainCGPipeline, Level); - // Lastly, add the core function simplification pipeline nested inside the + // Add the core function simplification pipeline nested inside the // CGSCC walk. MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor( buildFunctionSimplificationPipeline(Level, Phase), PTO.EagerlyInvalidateAnalyses, /*NoRerun=*/true)); + // Finally, deduce any function attributes based on the fully simplified function. + MainCGPipeline.addPass(PostOrderFunctionAttrsPass()); + + // Mark that the function is fully simplified and that it shouldn't be + // simplified again if we somehow revisit it due to CGSCC mutations unless + // it's been modified since. + MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor( + RequireAnalysisPass())); + MainCGPipeline.addPass(CoroSplitPass(Level != OptimizationLevel::O0)); + // Make sure we don't affect potential future NoRerun CGSCC adaptors. MIWP.addLateModulePass(createModuleToFunctionPassAdaptor( InvalidateAnalysisPass())); diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1763,6 +1763,13 @@ CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &) { + // Skip non-recursive functions if requested. + if (C.size() == 1 && SkipNonRecursive) { + LazyCallGraph::Node &N = *C.begin(); + if (!N->lookup(N)) + return PreservedAnalyses::all(); + } + FunctionAnalysisManager &FAM = AM.getResult(C, CG).getManager(); diff --git a/llvm/test/Transforms/InstCombine/unused-nonnull.ll b/llvm/test/Transforms/InstCombine/unused-nonnull.ll --- a/llvm/test/Transforms/InstCombine/unused-nonnull.ll +++ b/llvm/test/Transforms/InstCombine/unused-nonnull.ll @@ -9,7 +9,7 @@ define i32 @main(i32 %argc, ptr %argv) #0 { ; CHECK-LABEL: define {{[^@]+}}@main -; CHECK-SAME: (i32 [[ARGC:%.*]], ptr nocapture readonly [[ARGV:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-SAME: (i32 [[ARGC:%.*]], ptr nocapture readnone [[ARGV:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[ARGC]], 2 ; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[TMP0]], i32 0, i32 [[ARGC]]