From: Peter Szecsi Date: Wed, 19 Jul 2017 23:50:00 +0000 (+0000) Subject: This feature allows the analyzer to consider loops to completely unroll. New X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d76a2f16ee57554100bb3c5cbe87ebe590aca1ce;p=clang This feature allows the analyzer to consider loops to completely unroll. New requirements/rules (for unrolling) can be added easily via ASTMatchers. The current implementation is hidden behind a flag. Right now the blocks which belong to an unrolled loop are marked by the LoopVisitor which adds them to the ProgramState. Then whenever we encounter a CFGBlock in the processCFGBlockEntrance which is marked then we skip its investigating. That means, it won't be considered to be visited more than the maximal bound for visiting since it won't be checked. Differential Revision: https://reviews.llvm.org/D34260 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@308558 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 5dd6bdf384..f8de3bcb59 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -275,6 +275,9 @@ private: /// \sa shouldWidenLoops Optional WidenLoops; + /// \sa shouldUnrollLoops + Optional UnrollLoops; + /// \sa shouldDisplayNotesAsEvents Optional DisplayNotesAsEvents; @@ -560,7 +563,11 @@ public: /// This is controlled by the 'widen-loops' config option. bool shouldWidenLoops(); - /// Returns true if the bug reporter should transparently treat extra note + /// Returns true if the analysis should try to unroll loops with known bounds. + /// This is controlled by the 'unroll-loops' config option. + bool shouldUnrollLoops(); + + /// Returns true if the bug reporter should transparently treat extra note /// diagnostic pieces as event diagnostic pieces. Useful when the diagnostic /// consumer doesn't support the extra note pieces. /// diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 32040e7116..4a581bddd9 100644 --- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -272,6 +272,7 @@ void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, reportBug(llvm::to_string(NumTimesReached), BR, N); } + ReachedStats.clear(); } void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 6f48fcb9e2..d5d6527f4f 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -375,6 +375,12 @@ bool AnalyzerOptions::shouldWidenLoops() { return WidenLoops.getValue(); } +bool AnalyzerOptions::shouldUnrollLoops() { + if (!UnrollLoops.hasValue()) + UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false); + return UnrollLoops.getValue(); +} + bool AnalyzerOptions::shouldDisplayNotesAsEvents() { if (!DisplayNotesAsEvents.hasValue()) DisplayNotesAsEvents = diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 85878f5e96..5ac4f942f3 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -35,6 +35,7 @@ add_clang_library(clangStaticAnalyzerCore ExprEngineObjC.cpp FunctionSummary.cpp HTMLDiagnostics.cpp + LoopUnrolling.cpp LoopWidening.cpp MemRegion.cpp PathDiagnostic.cpp @@ -54,6 +55,7 @@ add_clang_library(clangStaticAnalyzerCore LINK_LIBS clangAST + clangASTMatchers clangAnalysis clangBasic clangLex diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index eee5400fe1..409fe173e2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -17,6 +17,7 @@ #include "PrettyStackTraceLocationContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/Builtins.h" @@ -27,6 +28,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" @@ -1497,6 +1499,27 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, NodeBuilderWithSinks &nodeBuilder, ExplodedNode *Pred) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + // If we reach a loop which has a known bound (and meets + // other constraints) then consider completely unrolling it. + if (AMgr.options.shouldUnrollLoops()) { + const CFGBlock *ActualBlock = nodeBuilder.getContext().getBlock(); + const Stmt *Term = ActualBlock->getTerminator(); + if (Term && shouldCompletelyUnroll(Term, AMgr.getASTContext())) { + ProgramStateRef UnrolledState = + markLoopAsUnrolled(Term, Pred->getState(), + Pred->getLocationContext() + ->getAnalysisDeclContext() + ->getCFGStmtMap()); + if (UnrolledState != Pred->getState()) + nodeBuilder.generateNode(UnrolledState, Pred); + return; + } + + if (isUnrolledLoopBlock(ActualBlock, Pred)) + return; + if (ActualBlock->empty()) + return; + } // If this block is terminated by a loop and it has already been visited the // maximum number of times, widen the loop. @@ -1664,7 +1687,6 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, const LocationContext *LCtx = Pred->getLocationContext(); PrettyStackTraceLocationContext StackCrashInfo(LCtx); currBldrCtx = &BldCtx; - // Check for NULL conditions; e.g. "for(;;)" if (!Condition) { BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 0d8d34fbf6..7bed7cb5e3 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -27,6 +27,7 @@ void foo() { // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 17 +// CHECK-NEXT: num-entries = 18 diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index 4166a730cf..155ee02e83 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -38,6 +38,7 @@ public: // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 22 +// CHECK-NEXT: num-entries = 23