@@ -2523,6 +2523,20 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
25232523 return true ;
25242524}
25252525
2526+ // / Return the innermost location context which is inlined at `Node`, unless
2527+ // / it's the top-level (entry point) location context.
2528+ static const LocationContext *getInlinedLocationContext (ExplodedNode *Node,
2529+ ExplodedGraph &G) {
2530+ const LocationContext *CalleeLC = Node->getLocation ().getLocationContext ();
2531+ const LocationContext *RootLC =
2532+ (*G.roots_begin ())->getLocation ().getLocationContext ();
2533+
2534+ if (CalleeLC->getStackFrame () == RootLC->getStackFrame ())
2535+ return nullptr ;
2536+
2537+ return CalleeLC;
2538+ }
2539+
25262540// / Block entrance. (Update counters).
25272541void ExprEngine::processCFGBlockEntrance (const BlockEdge &L,
25282542 NodeBuilderWithSinks &nodeBuilder,
@@ -2570,21 +2584,24 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
25702584 const ExplodedNode *Sink =
25712585 nodeBuilder.generateSink (Pred->getState (), Pred, &tag);
25722586
2573- // Check if we stopped at the top level function or not.
2574- // Root node should have the location context of the top most function.
2575- const LocationContext *CalleeLC = Pred->getLocation ().getLocationContext ();
2576- const LocationContext *CalleeSF = CalleeLC->getStackFrame ();
2577- const LocationContext *RootLC =
2578- (*G.roots_begin ())->getLocation ().getLocationContext ();
2579- if (RootLC->getStackFrame () != CalleeSF) {
2580- Engine.FunctionSummaries ->markReachedMaxBlockCount (CalleeSF->getDecl ());
2587+ if (const LocationContext *LC = getInlinedLocationContext (Pred, G)) {
2588+ // FIXME: This will unconditionally prevent inlining this function (even
2589+ // from other entry points), which is not a reasonable heuristic: even if
2590+ // we reached max block count on this particular execution path, there
2591+ // may be other execution paths (especially with other parametrizations)
2592+ // where the analyzer can reach the end of the function (so there is no
2593+ // natural reason to avoid inlining it). However, disabling this would
2594+ // significantly increase the analysis time (because more entry points
2595+ // would exhaust their allocated budget), so it must be compensated by a
2596+ // different (more reasonable) reduction of analysis scope.
2597+ Engine.FunctionSummaries ->markShouldNotInline (
2598+ LC->getStackFrame ()->getDecl ());
25812599
25822600 // Re-run the call evaluation without inlining it, by storing the
25832601 // no-inlining policy in the state and enqueuing the new work item on
25842602 // the list. Replay should almost never fail. Use the stats to catch it
25852603 // if it does.
2586- if ((!AMgr.options .NoRetryExhausted &&
2587- replayWithoutInlining (Pred, CalleeLC)))
2604+ if ((!AMgr.options .NoRetryExhausted && replayWithoutInlining (Pred, LC)))
25882605 return ;
25892606 NumMaxBlockCountReachedInInlined++;
25902607 } else
@@ -2856,8 +2873,29 @@ void ExprEngine::processBranch(
28562873 // conflicts with the widen-loop analysis option (which is off by
28572874 // default). If we intend to support and stabilize the loop widening,
28582875 // we must ensure that it 'plays nicely' with this logic.
2859- if (!SkipTrueBranch || AMgr.options .ShouldWidenLoops )
2876+ if (!SkipTrueBranch || AMgr.options .ShouldWidenLoops ) {
28602877 Builder.generateNode (StTrue, true , PredN);
2878+ } else if (!AMgr.options .InlineFunctionsWithAmbiguousLoops ) {
2879+ // FIXME: There is an ancient and arbitrary heuristic in
2880+ // `ExprEngine::processCFGBlockEntrance` which prevents all further
2881+ // inlining of a function if it finds an execution path within that
2882+ // function which reaches the `MaxBlockVisitOnPath` limit (a/k/a
2883+ // `analyzer-max-loop`, by default four iterations in a loop). Adding
2884+ // this "don't assume third iteration" logic significantly increased
2885+ // the analysis runtime on some inputs because less functions were
2886+ // arbitrarily excluded from being inlined, so more entry points used
2887+ // up their full allocated budget. As a hacky compensation for this,
2888+ // here we apply the "should not inline" mark in cases when the loop
2889+ // could potentially reach the `MaxBlockVisitOnPath` limit without the
2890+ // "don't assume third iteration" logic. This slightly overcompensates
2891+ // (activates if the third iteration can be entered, and will not
2892+ // recognize cases where the fourth iteration would't be completed), but
2893+ // should be good enough for practical purposes.
2894+ if (const LocationContext *LC = getInlinedLocationContext (Pred, G)) {
2895+ Engine.FunctionSummaries ->markShouldNotInline (
2896+ LC->getStackFrame ()->getDecl ());
2897+ }
2898+ }
28612899 }
28622900
28632901 if (StFalse) {
0 commit comments