r311346 - [StaticAnalyzer] LoopUnrolling: Track a LoopStack in order to completely unroll specific loops

Peter Szecsi via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 21 09:32:57 PDT 2017


Author: szepet
Date: Mon Aug 21 09:32:57 2017
New Revision: 311346

URL: http://llvm.org/viewvc/llvm-project?rev=311346&view=rev
Log:
[StaticAnalyzer] LoopUnrolling: Track a LoopStack in order to completely unroll specific loops

The LoopExit CFG information provides the opportunity to not mark the loops but
having a stack which tracks if a loop is unrolled or not. So in case of
simulating a loop we just add it and the information if it meets the
requirements to be unrolled to the top of the stack.

Differential Revision: https://reviews.llvm.org/D35684

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
    cfe/trunk/test/Analysis/loop-unrolling.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h?rev=311346&r1=311345&r2=311346&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h Mon Aug 21 09:32:57 2017
@@ -8,7 +8,17 @@
 //===----------------------------------------------------------------------===//
 ///
 /// This header contains the declarations of functions which are used to decide
-/// which loops should be completely unrolled and mark them.
+/// which loops should be completely unrolled and mark their corresponding
+/// CFGBlocks. It is done by tracking a stack of loops in the ProgramState. This
+/// way specific loops can be marked as completely unrolled. For considering a
+/// loop to be completely unrolled it has to fulfill the following requirements:
+/// - Currently only forStmts can be considered.
+/// - The bound has to be known.
+/// - The counter variable has not escaped before/in the body of the loop and
+///   changed only in the increment statement corresponding to the loop. It also
+///   has to be initialized by a literal in the corresponding initStmt.
+/// - Does not contain goto, switch and returnStmt.
+///
 ///
 //===----------------------------------------------------------------------===//
 
@@ -18,17 +28,21 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
-
 namespace clang {
 namespace ento {
 class AnalysisManager;
 
-ProgramStateRef markLoopAsUnrolled(const Stmt *Term, ProgramStateRef State,
-                                   const FunctionDecl *FD);
-bool isUnrolledLoopBlock(const CFGBlock *Block, ExplodedNode *Pred,
-                         AnalysisManager &AMgr);
-bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
-                            ExplodedNode *Pred);
+/// Returns if the given State indicates that is inside a completely unrolled
+/// loop.
+bool isUnrolledState(ProgramStateRef State);
+
+/// Updates the stack of loops contained by the ProgramState.
+ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx,
+                                ExplodedNode* Pred);
+
+/// Updates the given ProgramState. In current implementation it removes the top
+/// element of the stack of loops.
+ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State);
 
 } // end namespace ento
 } // end namespace clang

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=311346&r1=311345&r2=311346&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Mon Aug 21 09:32:57 2017
@@ -519,10 +519,13 @@ void ExprEngine::ProcessLoopExit(const S
   ExplodedNodeSet Dst;
   Dst.Add(Pred);
   NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+  ProgramStateRef NewState = Pred->getState();
 
-  LoopExit PP(S, Pred->getLocationContext());
-  Bldr.generateNode(PP, Pred->getState(), Pred);
+  if(AMgr.options.shouldUnrollLoops())
+    NewState = processLoopEnd(S, NewState);
 
+  LoopExit PP(S, Pred->getLocationContext());
+  Bldr.generateNode(PP, NewState, Pred);
   // Enqueue the new nodes onto the work list.
   Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
 }
@@ -1519,22 +1522,17 @@ void ExprEngine::processCFGBlockEntrance
   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(), Pred)) {
-      ProgramStateRef UnrolledState = markLoopAsUnrolled(
-              Term, Pred->getState(),
-              cast<FunctionDecl>(Pred->getStackFrame()->getDecl()));
-      if (UnrolledState != Pred->getState())
-        nodeBuilder.generateNode(UnrolledState, Pred);
-      return;
+  if(AMgr.options.shouldUnrollLoops()) {
+    const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+    if (Term) {
+      ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(),
+                                                 Pred);
+      if (NewState != Pred->getState()){
+        Pred = nodeBuilder.generateNode(NewState, Pred);
+      }
     }
-
-    if (ActualBlock->empty())
-      return;
-
-    if (isUnrolledLoopBlock(ActualBlock, Pred, AMgr))
+    // Is we are inside an unrolled loop then no need the check the counters.
+    if(isUnrolledState(Pred->getState()))
       return;
   }
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/LoopUnrolling.cpp?rev=311346&r1=311345&r2=311346&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/LoopUnrolling.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/LoopUnrolling.cpp Mon Aug 21 09:32:57 2017
@@ -8,9 +8,8 @@
 //===----------------------------------------------------------------------===//
 ///
 /// This file contains functions which are used to decide if a loop worth to be
-/// unrolled. Moreover contains function which mark the loops which are unrolled
-/// and store them in ProgramState. During the analysis we check the analyzed
-/// blocks if they are part of an unrolled loop or reached from one.
+/// unrolled. Moreover, these functions manages the stack of loop which is
+/// tracked by the ProgramState.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -28,13 +27,41 @@ using namespace clang;
 using namespace ento;
 using namespace clang::ast_matchers;
 
-#define DEBUG_TYPE "LoopUnrolling"
+struct LoopState {
+private:
+  enum Kind { Normal, Unrolled } K;
+  const Stmt *LoopStmt;
+  const LocationContext *LCtx;
+  LoopState(Kind InK, const Stmt *S, const LocationContext *L)
+      : K(InK), LoopStmt(S), LCtx(L) {}
 
-STATISTIC(NumTimesLoopUnrolled,
-          "The # of times a loop has got completely unrolled");
+public:
+  static LoopState getNormal(const Stmt *S, const LocationContext *L) {
+    return LoopState(Normal, S, L);
+  }
+  static LoopState getUnrolled(const Stmt *S, const LocationContext *L) {
+    return LoopState(Unrolled, S, L);
+  }
+  bool isUnrolled() const { return K == Unrolled; }
+  const Stmt *getLoopStmt() const { return LoopStmt; }
+  const LocationContext *getLocationContext() const { return LCtx; }
+  bool operator==(const LoopState &X) const {
+    return K == X.K && LoopStmt == X.LoopStmt;
+  }
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(K);
+    ID.AddPointer(LoopStmt);
+    ID.AddPointer(LCtx);
+  }
+};
 
-REGISTER_MAP_WITH_PROGRAMSTATE(UnrolledLoops, const Stmt *,
-                               const FunctionDecl *)
+// The tracked stack of loops. The stack indicates that which loops the
+// simulated element contained by. The loops are marked depending if we decided
+// to unroll them.
+// TODO: The loop stack should not need to be in the program state since it is
+// lexical in nature. Instead, the stack of loops should be tracked in the
+// LocationContext.
+REGISTER_LIST_WITH_PROGRAMSTATE(LoopStack, LoopState)
 
 namespace clang {
 namespace ento {
@@ -43,6 +70,16 @@ static bool isLoopStmt(const Stmt *S) {
   return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S));
 }
 
+ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
+  auto LS = State->get<LoopStack>();
+  assert(!LS.isEmpty() && "Loop not added to the stack.");
+  assert(LoopStmt == LS.getHead().getLoopStmt() &&
+         "Loop is not on top of the stack.");
+
+  State = State->set<LoopStack>(LS.getTail());
+  return State;
+}
+
 static internal::Matcher<Stmt> simpleCondition(StringRef BindName) {
   return binaryOperator(
       anyOf(hasOperatorName("<"), hasOperatorName(">"), hasOperatorName("<="),
@@ -90,7 +127,7 @@ getAddrTo(internal::Matcher<Decl> VarNod
 
 static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
   return hasDescendant(stmt(
-      anyOf(gotoStmt(), switchStmt(),
+      anyOf(gotoStmt(), switchStmt(), returnStmt(),
             // Escaping and not known mutation of the loop counter is handled
             // by exclusion of assigning and address-of operators and
             // pass-by-ref function calls on the loop counter from the body.
@@ -174,83 +211,34 @@ bool shouldCompletelyUnroll(const Stmt *
   return !isPossiblyEscaped(CounterVar->getCanonicalDecl(), Pred);
 }
 
-namespace {
-class LoopBlockVisitor : public ConstStmtVisitor<LoopBlockVisitor> {
-public:
-  LoopBlockVisitor(llvm::SmallPtrSet<const CFGBlock *, 8> &BS) : BlockSet(BS) {}
-
-  void VisitChildren(const Stmt *S) {
-    for (const Stmt *Child : S->children())
-      if (Child)
-        Visit(Child);
-  }
-
-  void VisitStmt(const Stmt *S) {
-    // In case of nested loops we only unroll the inner loop if it's marked too.
-    if (!S || (isLoopStmt(S) && S != LoopStmt))
-      return;
-    BlockSet.insert(StmtToBlockMap->getBlock(S));
-    VisitChildren(S);
-  }
-
-  void setBlocksOfLoop(const Stmt *Loop, const CFGStmtMap *M) {
-    BlockSet.clear();
-    StmtToBlockMap = M;
-    LoopStmt = Loop;
-    Visit(LoopStmt);
-  }
-
-private:
-  llvm::SmallPtrSet<const CFGBlock *, 8> &BlockSet;
-  const CFGStmtMap *StmtToBlockMap;
-  const Stmt *LoopStmt;
-};
-}
-// TODO: refactor this function using LoopExit CFG element - once we have the
-// information when the simulation reaches the end of the loop we can cleanup
-// the state
-bool isUnrolledLoopBlock(const CFGBlock *Block, ExplodedNode *Pred,
-                         AnalysisManager &AMgr) {
-  const Stmt *Term = Block->getTerminator();
+// updateLoopStack is called on every basic block, therefore it needs to be fast
+ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx,
+                                ExplodedNode* Pred) {
   auto State = Pred->getState();
-  // In case of nested loops in an inlined function should not be unrolled only
-  // if the inner loop is marked.
-  if (Term && isLoopStmt(Term) && !State->contains<UnrolledLoops>(Term))
-    return false;
+  auto LCtx = Pred->getLocationContext();
 
-  const CFGBlock *SearchedBlock;
-  llvm::SmallPtrSet<const CFGBlock *, 8> BlockSet;
-  LoopBlockVisitor LBV(BlockSet);
-  // Check the CFGBlocks of every marked loop.
-  for (auto &E : State->get<UnrolledLoops>()) {
-    SearchedBlock = Block;
-    const StackFrameContext *StackFrame = Pred->getStackFrame();
-    ParentMap PM(E.second->getBody());
-    CFGStmtMap *M = CFGStmtMap::Build(AMgr.getCFG(E.second), &PM);
-    LBV.setBlocksOfLoop(E.first, M);
-    // In case of an inlined function call check if any of its callSiteBlock is
-    // marked.
-    while (BlockSet.find(SearchedBlock) == BlockSet.end() && StackFrame) {
-      SearchedBlock = StackFrame->getCallSiteBlock();
-      if (!SearchedBlock || StackFrame->inTopFrame())
-        break;
-      StackFrame = StackFrame->getParent()->getCurrentStackFrame();
-    }
-    delete M;
-    if (SearchedBlock)
-      return true;
-  }
-  return false;
-}
+  if (!isLoopStmt(LoopStmt))
+    return State;
+
+  auto LS = State->get<LoopStack>();
+  if (!LS.isEmpty() && LoopStmt == LS.getHead().getLoopStmt() &&
+      LCtx == LS.getHead().getLocationContext())
+    return State;
 
-ProgramStateRef markLoopAsUnrolled(const Stmt *Term, ProgramStateRef State,
-                                   const FunctionDecl *FD) {
-  if (State->contains<UnrolledLoops>(Term))
+  if (!shouldCompletelyUnroll(LoopStmt, ASTCtx, Pred)) {
+    State = State->add<LoopStack>(LoopState::getNormal(LoopStmt, LCtx));
     return State;
+  }
 
-  State = State->set<UnrolledLoops>(Term, FD);
-  ++NumTimesLoopUnrolled;
+  State = State->add<LoopStack>(LoopState::getUnrolled(LoopStmt, LCtx));
   return State;
 }
+
+bool isUnrolledState(ProgramStateRef State) {
+  auto LS = State->get<LoopStack>();
+  if (LS.isEmpty() || !LS.getHead().isUnrolled())
+    return false;
+  return true;
+}
 }
 }

Modified: cfe/trunk/test/Analysis/loop-unrolling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/loop-unrolling.cpp?rev=311346&r1=311345&r2=311346&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/loop-unrolling.cpp (original)
+++ cfe/trunk/test/Analysis/loop-unrolling.cpp Mon Aug 21 09:32:57 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-max-loop 4 -analyzer-config unroll-loops=true -verify -std=c++11 %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config unroll-loops=true,cfg-loopexit=true -verify -std=c++11 %s
 
 void clang_analyzer_numTimesReached();
 
@@ -219,3 +219,56 @@ int nested_inlined_no_unroll1() {
   int a = 22 / k; // no-warning
   return 0;
 }
+
+
+int recursion_unroll1(bool b) {
+  int k = 2;
+  for (int i = 0; i < 5; i++) {
+    clang_analyzer_numTimesReached(); // expected-warning {{14}}
+    if(i == 0 && b)
+      recursion_unroll1(false);
+    clang_analyzer_numTimesReached(); // expected-warning {{15}}
+  }
+  int a = 22 / k; // no-warning
+  return 0;
+}
+
+int recursion_unroll2(bool b) {
+  int k = 0;
+  for (int i = 0; i < 5; i++) {
+    clang_analyzer_numTimesReached(); // expected-warning {{10}}
+    if(i == 0 && b)
+      recursion_unroll2(false);
+    clang_analyzer_numTimesReached(); // expected-warning {{10}}
+  }
+  int a = 22 / k; // expected-warning {{Division by zero}}
+  return 0;
+}
+
+int recursion_unroll3(bool b) {
+  int k = 2;
+  for (int i = 0; i < 5; i++) {
+    clang_analyzer_numTimesReached(); // expected-warning {{10}}
+    if(i == 4 && b) {
+      recursion_unroll3(false);
+      break;
+    }
+    clang_analyzer_numTimesReached(); // expected-warning {{10}}
+  }
+  int a = 22 / k;
+  return 0;
+}
+
+int recursion_unroll4(bool b) {
+  int k = 2;
+  for (int i = 0; i < 5; i++) {
+    clang_analyzer_numTimesReached(); // expected-warning {{14}}
+    if(i == 0 && b) {
+      recursion_unroll4(false);
+      continue;
+    }
+    clang_analyzer_numTimesReached(); // expected-warning {{14}}
+  }
+  int a = 22 / k;
+  return 0;
+}




More information about the cfe-commits mailing list