@@ -2067,6 +2067,36 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
20672067 AI.run (SCCNodes, Changed);
20682068}
20692069
2070+ // Determines if the function 'F' can be marked 'norecurse'.
2071+ // It returns true if any call within 'F' could lead to a recursive
2072+ // call back to 'F', and false otherwise.
2073+ // The 'AnyFunctionsAddressIsTaken' parameter is a module-wide flag
2074+ // that is true if any function's address is taken, or if any function
2075+ // has external linkage. This is used to determine the safety of
2076+ // external/library calls.
2077+ static bool hasRecursiveCallee (Function &F,
2078+ bool AnyFunctionsAddressIsTaken = true ) {
2079+ for (const auto &BB : F) {
2080+ for (const auto &I : BB.instructionsWithoutDebug ()) {
2081+ if (const auto *CB = dyn_cast<CallBase>(&I)) {
2082+ const Function *Callee = CB->getCalledFunction ();
2083+ if (!Callee || Callee == &F)
2084+ return true ;
2085+
2086+ if (Callee->doesNotRecurse ())
2087+ continue ;
2088+
2089+ if (!AnyFunctionsAddressIsTaken ||
2090+ (Callee->isDeclaration () &&
2091+ Callee->hasFnAttribute (Attribute::NoCallback)))
2092+ continue ;
2093+ return true ;
2094+ }
2095+ }
2096+ }
2097+ return false ;
2098+ }
2099+
20702100static void addNoRecurseAttrs (const SCCNodeSet &SCCNodes,
20712101 SmallPtrSet<Function *, 8 > &Changed) {
20722102 // Try and identify functions that do not recurse.
@@ -2078,28 +2108,14 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
20782108 Function *F = *SCCNodes.begin ();
20792109 if (!F || !F->hasExactDefinition () || F->doesNotRecurse ())
20802110 return ;
2081-
2082- // If all of the calls in F are identifiable and are to norecurse functions, F
2083- // is norecurse. This check also detects self-recursion as F is not currently
2084- // marked norecurse, so any called from F to F will not be marked norecurse.
2085- for (auto &BB : *F)
2086- for (auto &I : BB.instructionsWithoutDebug ())
2087- if (auto *CB = dyn_cast<CallBase>(&I)) {
2088- Function *Callee = CB->getCalledFunction ();
2089- if (!Callee || Callee == F ||
2090- (!Callee->doesNotRecurse () &&
2091- !(Callee->isDeclaration () &&
2092- Callee->hasFnAttribute (Attribute::NoCallback))))
2093- // Function calls a potentially recursive function.
2094- return ;
2095- }
2096-
2097- // Every call was to a non-recursive function other than this function, and
2098- // we have no indirect recursion as the SCC size is one. This function cannot
2099- // recurse.
2100- F->setDoesNotRecurse ();
2101- ++NumNoRecurse;
2102- Changed.insert (F);
2111+ if (!hasRecursiveCallee (*F)) {
2112+ // Every call was to a non-recursive function other than this function, and
2113+ // we have no indirect recursion as the SCC size is one. This function
2114+ // cannot recurse.
2115+ F->setDoesNotRecurse ();
2116+ ++NumNoRecurse;
2117+ Changed.insert (F);
2118+ }
21032119}
21042120
21052121// Set the noreturn function attribute if possible.
@@ -2429,3 +2445,66 @@ ReversePostOrderFunctionAttrsPass::run(Module &M, ModuleAnalysisManager &AM) {
24292445 PA.preserve <LazyCallGraphAnalysis>();
24302446 return PA;
24312447}
2448+
2449+ PreservedAnalyses NoRecurseLTOInferencePass::run (Module &M,
2450+ ModuleAnalysisManager &MAM) {
2451+
2452+ // Check if any function in the whole program has its address taken or has
2453+ // potentially external linkage.
2454+ // We use this information when inferring norecurse attribute: If there is
2455+ // no function whose address is taken and all functions have internal
2456+ // linkage, there is no path for a callback to any user function.
2457+ bool AnyFunctionsAddressIsTaken = false ;
2458+ for (Function &F : M) {
2459+ if (F.isDeclaration () || F.doesNotRecurse ()) {
2460+ continue ;
2461+ }
2462+ if (!F.hasLocalLinkage () || F.hasAddressTaken ()) {
2463+ AnyFunctionsAddressIsTaken = true ;
2464+ break ;
2465+ }
2466+ }
2467+
2468+ // Run norecurse inference on all RefSCCs in the LazyCallGraph for this
2469+ // module.
2470+ bool Changed = false ;
2471+ LazyCallGraph &CG = MAM.getResult <LazyCallGraphAnalysis>(M);
2472+ CG.buildRefSCCs ();
2473+
2474+ for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs ()) {
2475+ // Skip any RefSCC that is part of a call cycle. A RefSCC containing more
2476+ // than one SCC indicates a recursive relationship, which could involve
2477+ // direct or indirect calls.
2478+ if (RC.size () > 1 ) {
2479+ continue ;
2480+ }
2481+
2482+ // A single-SCC RefSCC could still be a self-loop.
2483+ LazyCallGraph::SCC &S = *RC.begin ();
2484+ if (S.size () > 1 ) {
2485+ continue ;
2486+ }
2487+
2488+ // Get the single function from this SCC.
2489+ Function &F = S.begin ()->getFunction ();
2490+ if (!F.hasExactDefinition () || F.doesNotRecurse ()) {
2491+ continue ;
2492+ }
2493+
2494+ // If the analysis confirms that this function has no recursive calls
2495+ // (either direct, indirect, or through external linkages),
2496+ // we can safely apply the norecurse attribute.
2497+ if (!hasRecursiveCallee (F, AnyFunctionsAddressIsTaken)) {
2498+ F.setDoesNotRecurse ();
2499+ ++NumNoRecurse;
2500+ Changed = true ;
2501+ }
2502+ }
2503+
2504+ PreservedAnalyses PA;
2505+ if (Changed)
2506+ PA.preserve <LazyCallGraphAnalysis>();
2507+ else
2508+ PA = PreservedAnalyses::all ();
2509+ return PA;
2510+ }
0 commit comments