[PATCH] Sema: Eliminate recursion from checkForFunctionCall()
Duncan P. N. Exon Smith
dexonsmith at apple.com
Wed Jul 22 14:04:08 PDT 2015
> On 2015-Jul-22, at 13:34, Vedant Kumar <vsk at apple.com> wrote:
(I had this reply already written when you sent it to llvm-commits; I
didn't even notice it was the wrong list. I just copy/pasted, so I
hope it's the same patch.)
>
> Large CFGs cause clang::Sema::checkForFunctionCall() to stack overflow.
>
> I have a patch which breaks the recursion in checkForFunctionCall() by manually
> managing the call stack. This fixes the problem. The check-clang target reports
> no regressions.
>
> I don't have commit rights, so I'd appreciate someone taking a look at the
> patch and getting it into trunk :).
>
In future, please attach patches an attachments. As you can see by
the strange whitespace on lines without a -/+ in my reply below, some
mailers don't handle inline patches very well.
> thanks!
> vedant
>
> ================================================================================
>
> diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
> index f2ff48a..dc6dda0 100644
> --- a/lib/Sema/AnalysisBasedWarnings.cpp
> +++ b/lib/Sema/AnalysisBasedWarnings.cpp
> @@ -52,6 +52,8 @@
> #include <deque>
> #include <iterator>
> #include <vector>
> +#include <functional>
> +#include <utility>
>
> using namespace clang;
>
> @@ -175,59 +177,69 @@ static void checkForFunctionCall(Sema &S, const FunctionDecl *FD,
> CFGBlock &Block, unsigned ExitID,
> llvm::SmallVectorImpl<RecursiveState> &States,
> RecursiveState State) {
> - unsigned ID = Block.getBlockID();
> + std::vector<std::pair<std::reference_wrapper<CFGBlock>, RecursiveState>>
> + Stack;
Probably a `SmallVector<..., 16>` or some such would make sense
instead of a `std::vector<...>`.
I haven't seen `reference_wrapper<>` at all in LLVM, so I'm not sure
all the supported standard libraries have it available. Please use
`CFGBlock*` instead.
> + Stack.emplace_back(std::make_pair(std::ref(Block), State));
With `emplace_back()`, you can call any constructor:
Stack.emplace_back(&Block, State);
>
> - // A block's state can only move to a higher state.
> - if (States[ID] >= State)
> - return;
> + while (!Stack.empty()) {
> + CFGBlock &CurBlock = Stack.back().first.get();
(IIRC, reference_wrapper has an implicit conversion to `&`, so you
wouldn't actually need the `.get()` here.)
> + RecursiveState CurState = Stack.back().second;
> + Stack.pop_back();
>
> - States[ID] = State;
> + unsigned ID = CurBlock.getBlockID();
>
> - // Found a path to the exit node without a recursive call.
> - if (ID == ExitID && State == FoundPathWithNoRecursiveCall)
> - return;
> + // A block's state can only move to a higher state.
> + if (States[ID] >= CurState)
> + continue;
>
> - if (State == FoundPathWithNoRecursiveCall) {
> - // If the current state is FoundPathWithNoRecursiveCall, the successors
> - // will be either FoundPathWithNoRecursiveCall or FoundPath. To determine
> - // which, process all the Stmt's in this block to find any recursive calls.
> - for (const auto &B : Block) {
> - if (B.getKind() != CFGElement::Statement)
> - continue;
> + States[ID] = CurState;
> +
> + // Found a path to the exit node without a recursive call.
> + if (ID == ExitID && CurState == FoundPathWithNoRecursiveCall)
> + continue;
> +
> + if (CurState == FoundPathWithNoRecursiveCall) {
This is a fairly large block of code to further indent. Can you
move this to a helper function as a separate, preparatory patch
with no functionality change (NFC) that returns the new value of
`State`?
> + // If the current state is FoundPathWithNoRecursiveCall, the successors
> + // will be either FoundPathWithNoRecursiveCall or FoundPath. To determine
> + // which, process all the Stmt's in this block to find any recursive
> + // calls.
> + for (const auto &B : CurBlock) {
> + if (B.getKind() != CFGElement::Statement)
> + continue;
>
> - const CallExpr *CE = dyn_cast<CallExpr>(B.getAs<CFGStmt>()->getStmt());
> - if (CE && CE->getCalleeDecl() &&
> - CE->getCalleeDecl()->getCanonicalDecl() == FD) {
> -
> - // Skip function calls which are qualified with a templated class.
> - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(
> - CE->getCallee()->IgnoreParenImpCasts())) {
> - if (NestedNameSpecifier *NNS = DRE->getQualifier()) {
> - if (NNS->getKind() == NestedNameSpecifier::TypeSpec &&
> - isa<TemplateSpecializationType>(NNS->getAsType())) {
> - continue;
> + const CallExpr *CE = dyn_cast<CallExpr>(B.getAs<CFGStmt>()->getStmt());
> + if (CE && CE->getCalleeDecl() &&
> + CE->getCalleeDecl()->getCanonicalDecl() == FD) {
> +
> + // Skip function calls which are qualified with a templated class.
> + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(
> + CE->getCallee()->IgnoreParenImpCasts())) {
> + if (NestedNameSpecifier *NNS = DRE->getQualifier()) {
> + if (NNS->getKind() == NestedNameSpecifier::TypeSpec &&
> + isa<TemplateSpecializationType>(NNS->getAsType())) {
> + continue;
> + }
> }
> }
> - }
>
> - if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
> - if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) ||
> - !MCE->getMethodDecl()->isVirtual()) {
> - State = FoundPath;
> + if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
> + if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) ||
> + !MCE->getMethodDecl()->isVirtual()) {
> + CurState = FoundPath;
> + break;
> + }
> + } else {
> + CurState = FoundPath;
> break;
> }
> - } else {
> - State = FoundPath;
> - break;
> }
> }
> }
> - }
>
> - for (CFGBlock::succ_iterator I = Block.succ_begin(), E = Block.succ_end();
> - I != E; ++I)
> - if (*I)
> - checkForFunctionCall(S, FD, **I, ExitID, States, State);
> + for (auto I = CurBlock.succ_rbegin(), E = CurBlock.succ_rend(); I != E; ++I)
> + if (*I)
> + Stack.emplace_back(std::make_pair(std::ref(**I), CurState));
I see you've maintained the visitation order by reversing iteration.
Makes sense for this commit, but it might be nice to clean this up
in a follow-up (assuming visitation order doesn't matter here, which
I don't think it does?):
for (BasicBlock *BB : CurBlock.successors())
if (BB)
Stack.emplace_back(BB, CurState);
(You'd have to add the `CFGBlock::successors()` API.)
> + }
> }
>
> static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD,
>
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the cfe-commits
mailing list