@@ -2067,8 +2067,49 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
2067
2067
AI.run (SCCNodes, Changed);
2068
2068
}
2069
2069
2070
+ // / Returns true if N or any function it (transitively) calls makes a call
2071
+ // / to an unknown external function: either an indirect call or a declaration
2072
+ // / without the NoCallback attribute.
2073
+ static bool callsUnknownExternal (LazyCallGraph::Node *N) {
2074
+ std::deque<LazyCallGraph::Node *> Worklist;
2075
+ DenseSet<LazyCallGraph::Node *> Visited;
2076
+ Worklist.push_back (N);
2077
+ Visited.insert (N);
2078
+
2079
+ while (!Worklist.empty ()) {
2080
+ auto *Cur = Worklist.front ();
2081
+ Worklist.pop_front ();
2082
+
2083
+ Function &F = Cur->getFunction ();
2084
+ for (auto &BB : F) {
2085
+ for (auto &I : BB.instructionsWithoutDebug ()) {
2086
+ if (auto *CB = dyn_cast<CallBase>(&I)) {
2087
+ const Function *Callee = CB->getCalledFunction ();
2088
+ // Indirect call, treat as unknown external function.
2089
+ if (!Callee)
2090
+ return true ;
2091
+ // Extern declaration without NoCallback attribute
2092
+ if (Callee->isDeclaration () &&
2093
+ !Callee->hasFnAttribute (Attribute::NoCallback))
2094
+ return true ;
2095
+ }
2096
+ }
2097
+ }
2098
+
2099
+ // Enqueue all direct call-edge successors for further scanning
2100
+ for (auto &Edge : Cur->populate ().calls ()) {
2101
+ LazyCallGraph::Node *Succ = &Edge.getNode ();
2102
+ if (Visited.insert (Succ).second )
2103
+ Worklist.push_back (Succ);
2104
+ }
2105
+ }
2106
+
2107
+ return false ;
2108
+ }
2109
+
2070
2110
static void addNoRecurseAttrs (const SCCNodeSet &SCCNodes,
2071
- SmallPtrSet<Function *, 8 > &Changed) {
2111
+ SmallPtrSet<Function *, 8 > &Changed,
2112
+ LazyCallGraph &CG) {
2072
2113
// Try and identify functions that do not recurse.
2073
2114
2074
2115
// If the SCC contains multiple nodes we know for sure there is recursion.
@@ -2079,24 +2120,35 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
2079
2120
if (!F || !F->hasExactDefinition () || F->doesNotRecurse ())
2080
2121
return ;
2081
2122
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 ())
2123
+ // If all of the calls in F are identifiable and can be proven to not
2124
+ // callback F, F is norecurse. This check also detects self-recursion
2125
+ // as F is not currently marked norecurse, so any call from F to F
2126
+ // will not be marked norecurse.
2127
+ for (auto &BB : *F) {
2128
+ for (auto &I : BB.instructionsWithoutDebug ()) {
2087
2129
if (auto *CB = dyn_cast<CallBase>(&I)) {
2088
2130
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.
2131
+
2132
+ if (!Callee || Callee == F)
2133
+ return ;
2134
+
2135
+ // External call with NoCallback attribute.
2136
+ if (Callee->isDeclaration ()) {
2137
+ if (Callee->hasFnAttribute (Attribute::NoCallback))
2138
+ continue ;
2094
2139
return ;
2140
+ }
2141
+
2142
+ if (auto *CNode = CG.lookup (*Callee))
2143
+ if (callsUnknownExternal (CNode))
2144
+ return ;
2095
2145
}
2146
+ }
2147
+ }
2096
2148
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.
2149
+ // Every call was either to an external function guaranteed to not make a
2150
+ // call to this function or a direct call to internal function and we have no
2151
+ // indirect recursion as the SCC size is one. This function cannot recurse.
2100
2152
F->setDoesNotRecurse ();
2101
2153
++NumNoRecurse;
2102
2154
Changed.insert (F);
@@ -2240,7 +2292,7 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
2240
2292
template <typename AARGetterT>
2241
2293
static SmallPtrSet<Function *, 8 >
2242
2294
deriveAttrsInPostOrder (ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
2243
- bool ArgAttrsOnly) {
2295
+ bool ArgAttrsOnly, LazyCallGraph &CG ) {
2244
2296
SCCNodesResult Nodes = createSCCNodeSet (Functions);
2245
2297
2246
2298
// Bail if the SCC only contains optnone functions.
@@ -2264,10 +2316,13 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
2264
2316
addColdAttrs (Nodes.SCCNodes , Changed);
2265
2317
addWillReturn (Nodes.SCCNodes , Changed);
2266
2318
addNoUndefAttrs (Nodes.SCCNodes , Changed);
2267
- addNoAliasAttrs (Nodes.SCCNodes , Changed);
2268
- addNonNullAttrs (Nodes.SCCNodes , Changed);
2269
- inferAttrsFromFunctionBodies (Nodes.SCCNodes , Changed);
2270
- addNoRecurseAttrs (Nodes.SCCNodes , Changed);
2319
+
2320
+ // If we have no external nodes participating in the SCC, we can deduce some
2321
+ // more precise attributes as well.
2322
+ addNoAliasAttrs (Nodes.SCCNodes , Changed);
2323
+ addNonNullAttrs (Nodes.SCCNodes , Changed);
2324
+ inferAttrsFromFunctionBodies (Nodes.SCCNodes , Changed);
2325
+ addNoRecurseAttrs (Nodes.SCCNodes , Changed, CG);
2271
2326
2272
2327
// Finally, infer the maximal set of attributes from the ones we've inferred
2273
2328
// above. This is handling the cases where one attribute on a signature
@@ -2310,7 +2365,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
2310
2365
}
2311
2366
2312
2367
auto ChangedFunctions =
2313
- deriveAttrsInPostOrder (Functions, AARGetter, ArgAttrsOnly);
2368
+ deriveAttrsInPostOrder (Functions, AARGetter, ArgAttrsOnly, CG );
2314
2369
if (ChangedFunctions.empty ())
2315
2370
return PreservedAnalyses::all ();
2316
2371
0 commit comments