@@ -2510,6 +2510,20 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
25102510 return true ;
25112511}
25122512
2513+ // / Return the innermost location context which is inlined at `Node`, unless
2514+ // / it's the top-level (entry point) location context.
2515+ static const LocationContext *getInlinedLocationContext (ExplodedNode *Node,
2516+ ExplodedGraph &G) {
2517+ const LocationContext *CalleeLC = Node->getLocation ().getLocationContext ();
2518+ const LocationContext *RootLC =
2519+ (*G.roots_begin ())->getLocation ().getLocationContext ();
2520+
2521+ if (CalleeLC->getStackFrame () == RootLC->getStackFrame ())
2522+ return nullptr ;
2523+
2524+ return CalleeLC;
2525+ }
2526+
25132527// / Block entrance. (Update counters).
25142528void ExprEngine::processCFGBlockEntrance (const BlockEdge &L,
25152529 NodeBuilderWithSinks &nodeBuilder,
@@ -2557,21 +2571,24 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
25572571 const ExplodedNode *Sink =
25582572 nodeBuilder.generateSink (Pred->getState (), Pred, &tag);
25592573
2560- // Check if we stopped at the top level function or not.
2561- // Root node should have the location context of the top most function.
2562- const LocationContext *CalleeLC = Pred->getLocation ().getLocationContext ();
2563- const LocationContext *CalleeSF = CalleeLC->getStackFrame ();
2564- const LocationContext *RootLC =
2565- (*G.roots_begin ())->getLocation ().getLocationContext ();
2566- if (RootLC->getStackFrame () != CalleeSF) {
2567- Engine.FunctionSummaries ->markReachedMaxBlockCount (CalleeSF->getDecl ());
2574+ if (const LocationContext *LC = getInlinedLocationContext (Pred, G)) {
2575+ // FIXME: This will unconditionally prevent inlining this function (even
2576+ // from other entry points), which is not a reasonable heuristic: even if
2577+ // we reached max block count on this particular execution path, there
2578+ // may be other execution paths (especially with other parametrizations)
2579+ // where the analyzer can reach the end of the function (so there is no
2580+ // natural reason to avoid inlining it). However, disabling this would
2581+ // significantly increase the analysis time (because more entry points
2582+ // would exhaust their allocated budget), so it must be compensated by a
2583+ // different (more reasonable) reduction of analysis scope.
2584+ Engine.FunctionSummaries ->markShouldNotInline (
2585+ LC->getStackFrame ()->getDecl ());
25682586
25692587 // Re-run the call evaluation without inlining it, by storing the
25702588 // no-inlining policy in the state and enqueuing the new work item on
25712589 // the list. Replay should almost never fail. Use the stats to catch it
25722590 // if it does.
2573- if ((!AMgr.options .NoRetryExhausted &&
2574- replayWithoutInlining (Pred, CalleeLC)))
2591+ if ((!AMgr.options .NoRetryExhausted && replayWithoutInlining (Pred, LC)))
25752592 return ;
25762593 NumMaxBlockCountReachedInInlined++;
25772594 } else
@@ -2835,8 +2852,29 @@ void ExprEngine::processBranch(
28352852 // conflicts with the widen-loop analysis option (which is off by
28362853 // default). If we intend to support and stabilize the loop widening,
28372854 // we must ensure that it 'plays nicely' with this logic.
2838- if (!SkipTrueBranch || AMgr.options .ShouldWidenLoops )
2855+ if (!SkipTrueBranch || AMgr.options .ShouldWidenLoops ) {
28392856 Builder.generateNode (StTrue, true , PredN);
2857+ } else if (!AMgr.options .InlineFunctionsWithAmbiguousLoops ) {
2858+ // FIXME: There is an ancient and arbitrary heuristic in
2859+ // `ExprEngine::processCFGBlockEntrance` which prevents all further
2860+ // inlining of a function if it finds an execution path within that
2861+ // function which reaches the `MaxBlockVisitOnPath` limit (a/k/a
2862+ // `analyzer-max-loop`, by default four iterations in a loop). Adding
2863+ // this "don't assume third iteration" logic significantly increased
2864+ // the analysis runtime on some inputs because less functions were
2865+ // arbitrarily excluded from being inlined, so more entry points used
2866+ // up their full allocated budget. As a hacky compensation for this,
2867+ // here we apply the "should not inline" mark in cases when the loop
2868+ // could potentially reach the `MaxBlockVisitOnPath` limit without the
2869+ // "don't assume third iteration" logic. This slightly overcompensates
2870+ // (activates if the third iteration can be entered, and will not
2871+ // recognize cases where the fourth iteration would't be completed), but
2872+ // should be good enough for practical purposes.
2873+ if (const LocationContext *LC = getInlinedLocationContext (Pred, G)) {
2874+ Engine.FunctionSummaries ->markShouldNotInline (
2875+ LC->getStackFrame ()->getDecl ());
2876+ }
2877+ }
28402878 }
28412879
28422880 if (StFalse)
0 commit comments