xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// This file contains functions which are used to decide if a loop worth to be
100b57cec5SDimitry Andric /// unrolled. Moreover, these functions manages the stack of loop which is
110b57cec5SDimitry Andric /// tracked by the ProgramState.
120b57cec5SDimitry Andric ///
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
160b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
20bdd1243dSDimitry Andric #include <optional>
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace clang;
230b57cec5SDimitry Andric using namespace ento;
240b57cec5SDimitry Andric using namespace clang::ast_matchers;
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric static const int MAXIMUM_STEP_UNROLLED = 128;
270b57cec5SDimitry Andric 
28bdd1243dSDimitry Andric namespace {
290b57cec5SDimitry Andric struct LoopState {
300b57cec5SDimitry Andric private:
310b57cec5SDimitry Andric   enum Kind { Normal, Unrolled } K;
320b57cec5SDimitry Andric   const Stmt *LoopStmt;
330b57cec5SDimitry Andric   const LocationContext *LCtx;
340b57cec5SDimitry Andric   unsigned maxStep;
350b57cec5SDimitry Andric   LoopState(Kind InK, const Stmt *S, const LocationContext *L, unsigned N)
360b57cec5SDimitry Andric       : K(InK), LoopStmt(S), LCtx(L), maxStep(N) {}
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric public:
390b57cec5SDimitry Andric   static LoopState getNormal(const Stmt *S, const LocationContext *L,
400b57cec5SDimitry Andric                              unsigned N) {
410b57cec5SDimitry Andric     return LoopState(Normal, S, L, N);
420b57cec5SDimitry Andric   }
430b57cec5SDimitry Andric   static LoopState getUnrolled(const Stmt *S, const LocationContext *L,
440b57cec5SDimitry Andric                                unsigned N) {
450b57cec5SDimitry Andric     return LoopState(Unrolled, S, L, N);
460b57cec5SDimitry Andric   }
470b57cec5SDimitry Andric   bool isUnrolled() const { return K == Unrolled; }
480b57cec5SDimitry Andric   unsigned getMaxStep() const { return maxStep; }
490b57cec5SDimitry Andric   const Stmt *getLoopStmt() const { return LoopStmt; }
500b57cec5SDimitry Andric   const LocationContext *getLocationContext() const { return LCtx; }
510b57cec5SDimitry Andric   bool operator==(const LoopState &X) const {
520b57cec5SDimitry Andric     return K == X.K && LoopStmt == X.LoopStmt;
530b57cec5SDimitry Andric   }
540b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
550b57cec5SDimitry Andric     ID.AddInteger(K);
560b57cec5SDimitry Andric     ID.AddPointer(LoopStmt);
570b57cec5SDimitry Andric     ID.AddPointer(LCtx);
580b57cec5SDimitry Andric     ID.AddInteger(maxStep);
590b57cec5SDimitry Andric   }
600b57cec5SDimitry Andric };
61bdd1243dSDimitry Andric } // namespace
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric // The tracked stack of loops. The stack indicates that which loops the
640b57cec5SDimitry Andric // simulated element contained by. The loops are marked depending if we decided
650b57cec5SDimitry Andric // to unroll them.
660b57cec5SDimitry Andric // TODO: The loop stack should not need to be in the program state since it is
670b57cec5SDimitry Andric // lexical in nature. Instead, the stack of loops should be tracked in the
680b57cec5SDimitry Andric // LocationContext.
690b57cec5SDimitry Andric REGISTER_LIST_WITH_PROGRAMSTATE(LoopStack, LoopState)
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric namespace clang {
720b57cec5SDimitry Andric namespace ento {
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric static bool isLoopStmt(const Stmt *S) {
75349cc55cSDimitry Andric   return isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(S);
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
790b57cec5SDimitry Andric   auto LS = State->get<LoopStack>();
800b57cec5SDimitry Andric   if (!LS.isEmpty() && LS.getHead().getLoopStmt() == LoopStmt)
810b57cec5SDimitry Andric     State = State->set<LoopStack>(LS.getTail());
820b57cec5SDimitry Andric   return State;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric 
85fe6060f1SDimitry Andric static internal::Matcher<Stmt> simpleCondition(StringRef BindName,
86fe6060f1SDimitry Andric                                                StringRef RefName) {
87fe6060f1SDimitry Andric   return binaryOperator(
88fe6060f1SDimitry Andric              anyOf(hasOperatorName("<"), hasOperatorName(">"),
890b57cec5SDimitry Andric                    hasOperatorName("<="), hasOperatorName(">="),
900b57cec5SDimitry Andric                    hasOperatorName("!=")),
910b57cec5SDimitry Andric              hasEitherOperand(ignoringParenImpCasts(
92fe6060f1SDimitry Andric                  declRefExpr(to(varDecl(hasType(isInteger())).bind(BindName)))
93fe6060f1SDimitry Andric                      .bind(RefName))),
94fe6060f1SDimitry Andric              hasEitherOperand(
95fe6060f1SDimitry Andric                  ignoringParenImpCasts(integerLiteral().bind("boundNum"))))
960b57cec5SDimitry Andric       .bind("conditionOperator");
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric static internal::Matcher<Stmt>
1000b57cec5SDimitry Andric changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) {
1010b57cec5SDimitry Andric   return anyOf(
1020b57cec5SDimitry Andric       unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")),
1030b57cec5SDimitry Andric                     hasUnaryOperand(ignoringParenImpCasts(
1040b57cec5SDimitry Andric                         declRefExpr(to(varDecl(VarNodeMatcher)))))),
1050b57cec5SDimitry Andric       binaryOperator(isAssignmentOperator(),
1060b57cec5SDimitry Andric                      hasLHS(ignoringParenImpCasts(
1070b57cec5SDimitry Andric                          declRefExpr(to(varDecl(VarNodeMatcher)))))));
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric static internal::Matcher<Stmt>
1110b57cec5SDimitry Andric callByRef(internal::Matcher<Decl> VarNodeMatcher) {
1120b57cec5SDimitry Andric   return callExpr(forEachArgumentWithParam(
1130b57cec5SDimitry Andric       declRefExpr(to(varDecl(VarNodeMatcher))),
1140b57cec5SDimitry Andric       parmVarDecl(hasType(references(qualType(unless(isConstQualified())))))));
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric static internal::Matcher<Stmt>
1180b57cec5SDimitry Andric assignedToRef(internal::Matcher<Decl> VarNodeMatcher) {
1190b57cec5SDimitry Andric   return declStmt(hasDescendant(varDecl(
1200b57cec5SDimitry Andric       allOf(hasType(referenceType()),
1210b57cec5SDimitry Andric             hasInitializer(anyOf(
1220b57cec5SDimitry Andric                 initListExpr(has(declRefExpr(to(varDecl(VarNodeMatcher))))),
1230b57cec5SDimitry Andric                 declRefExpr(to(varDecl(VarNodeMatcher)))))))));
1240b57cec5SDimitry Andric }
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric static internal::Matcher<Stmt>
1270b57cec5SDimitry Andric getAddrTo(internal::Matcher<Decl> VarNodeMatcher) {
1280b57cec5SDimitry Andric   return unaryOperator(
1290b57cec5SDimitry Andric       hasOperatorName("&"),
1300b57cec5SDimitry Andric       hasUnaryOperand(declRefExpr(hasDeclaration(VarNodeMatcher))));
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
1340b57cec5SDimitry Andric   return hasDescendant(stmt(
1350b57cec5SDimitry Andric       anyOf(gotoStmt(), switchStmt(), returnStmt(),
1360b57cec5SDimitry Andric             // Escaping and not known mutation of the loop counter is handled
1370b57cec5SDimitry Andric             // by exclusion of assigning and address-of operators and
1380b57cec5SDimitry Andric             // pass-by-ref function calls on the loop counter from the body.
1395ffd83dbSDimitry Andric             changeIntBoundNode(equalsBoundNode(std::string(NodeName))),
1405ffd83dbSDimitry Andric             callByRef(equalsBoundNode(std::string(NodeName))),
1415ffd83dbSDimitry Andric             getAddrTo(equalsBoundNode(std::string(NodeName))),
1425ffd83dbSDimitry Andric             assignedToRef(equalsBoundNode(std::string(NodeName))))));
1430b57cec5SDimitry Andric }
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric static internal::Matcher<Stmt> forLoopMatcher() {
1460b57cec5SDimitry Andric   return forStmt(
147fe6060f1SDimitry Andric              hasCondition(simpleCondition("initVarName", "initVarRef")),
1480b57cec5SDimitry Andric              // Initialization should match the form: 'int i = 6' or 'i = 42'.
1490b57cec5SDimitry Andric              hasLoopInit(
1500b57cec5SDimitry Andric                  anyOf(declStmt(hasSingleDecl(
1510b57cec5SDimitry Andric                            varDecl(allOf(hasInitializer(ignoringParenImpCasts(
1520b57cec5SDimitry Andric                                              integerLiteral().bind("initNum"))),
1530b57cec5SDimitry Andric                                          equalsBoundNode("initVarName"))))),
1540b57cec5SDimitry Andric                        binaryOperator(hasLHS(declRefExpr(to(varDecl(
1550b57cec5SDimitry Andric                                           equalsBoundNode("initVarName"))))),
1560b57cec5SDimitry Andric                                       hasRHS(ignoringParenImpCasts(
1570b57cec5SDimitry Andric                                           integerLiteral().bind("initNum")))))),
1580b57cec5SDimitry Andric              // Incrementation should be a simple increment or decrement
1590b57cec5SDimitry Andric              // operator call.
1600b57cec5SDimitry Andric              hasIncrement(unaryOperator(
1610b57cec5SDimitry Andric                  anyOf(hasOperatorName("++"), hasOperatorName("--")),
1620b57cec5SDimitry Andric                  hasUnaryOperand(declRefExpr(
1630b57cec5SDimitry Andric                      to(varDecl(allOf(equalsBoundNode("initVarName"),
1640b57cec5SDimitry Andric                                       hasType(isInteger())))))))),
165fe6060f1SDimitry Andric              unless(hasBody(hasSuspiciousStmt("initVarName"))))
166fe6060f1SDimitry Andric       .bind("forLoop");
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric 
169fe6060f1SDimitry Andric static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) {
170fe6060f1SDimitry Andric 
171fe6060f1SDimitry Andric   // Get the lambda CXXRecordDecl
172fe6060f1SDimitry Andric   assert(DR->refersToEnclosingVariableOrCapture());
173fe6060f1SDimitry Andric   const LocationContext *LocCtxt = N->getLocationContext();
174fe6060f1SDimitry Andric   const Decl *D = LocCtxt->getDecl();
175fe6060f1SDimitry Andric   const auto *MD = cast<CXXMethodDecl>(D);
176fe6060f1SDimitry Andric   assert(MD && MD->getParent()->isLambda() &&
177fe6060f1SDimitry Andric          "Captured variable should only be seen while evaluating a lambda");
178fe6060f1SDimitry Andric   const CXXRecordDecl *LambdaCXXRec = MD->getParent();
179fe6060f1SDimitry Andric 
180fe6060f1SDimitry Andric   // Lookup the fields of the lambda
181bdd1243dSDimitry Andric   llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
182fe6060f1SDimitry Andric   FieldDecl *LambdaThisCaptureField;
183fe6060f1SDimitry Andric   LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
184fe6060f1SDimitry Andric 
185fe6060f1SDimitry Andric   // Check if the counter is captured by reference
186fe6060f1SDimitry Andric   const VarDecl *VD = cast<VarDecl>(DR->getDecl()->getCanonicalDecl());
187fe6060f1SDimitry Andric   assert(VD);
188fe6060f1SDimitry Andric   const FieldDecl *FD = LambdaCaptureFields[VD];
189fe6060f1SDimitry Andric   assert(FD && "Captured variable without a corresponding field");
190fe6060f1SDimitry Andric   return FD->getType()->isReferenceType();
191fe6060f1SDimitry Andric }
192fe6060f1SDimitry Andric 
193*0fca6ea1SDimitry Andric static bool isFoundInStmt(const Stmt *S, const VarDecl *VD) {
194*0fca6ea1SDimitry Andric   if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
195*0fca6ea1SDimitry Andric     for (const Decl *D : DS->decls()) {
196*0fca6ea1SDimitry Andric       // Once we reach the declaration of the VD we can return.
197*0fca6ea1SDimitry Andric       if (D->getCanonicalDecl() == VD)
198*0fca6ea1SDimitry Andric         return true;
199*0fca6ea1SDimitry Andric     }
200*0fca6ea1SDimitry Andric   }
201*0fca6ea1SDimitry Andric   return false;
202*0fca6ea1SDimitry Andric }
203*0fca6ea1SDimitry Andric 
204fe6060f1SDimitry Andric // A loop counter is considered escaped if:
205fe6060f1SDimitry Andric // case 1: It is a global variable.
206fe6060f1SDimitry Andric // case 2: It is a reference parameter or a reference capture.
207fe6060f1SDimitry Andric // case 3: It is assigned to a non-const reference variable or parameter.
208fe6060f1SDimitry Andric // case 4: Has its address taken.
209fe6060f1SDimitry Andric static bool isPossiblyEscaped(ExplodedNode *N, const DeclRefExpr *DR) {
210fe6060f1SDimitry Andric   const VarDecl *VD = cast<VarDecl>(DR->getDecl()->getCanonicalDecl());
211fe6060f1SDimitry Andric   assert(VD);
212fe6060f1SDimitry Andric   // Case 1:
2130b57cec5SDimitry Andric   if (VD->hasGlobalStorage())
2140b57cec5SDimitry Andric     return true;
2150b57cec5SDimitry Andric 
216fe6060f1SDimitry Andric   const bool IsRefParamOrCapture =
217fe6060f1SDimitry Andric       isa<ParmVarDecl>(VD) || DR->refersToEnclosingVariableOrCapture();
218fe6060f1SDimitry Andric   // Case 2:
219fe6060f1SDimitry Andric   if ((DR->refersToEnclosingVariableOrCapture() &&
220fe6060f1SDimitry Andric        isCapturedByReference(N, DR)) ||
221fe6060f1SDimitry Andric       (IsRefParamOrCapture && VD->getType()->isReferenceType()))
2225ffd83dbSDimitry Andric     return true;
2235ffd83dbSDimitry Andric 
2240b57cec5SDimitry Andric   while (!N->pred_empty()) {
225a7dea167SDimitry Andric     // FIXME: getStmtForDiagnostics() does nasty things in order to provide
226a7dea167SDimitry Andric     // a valid statement for body farms, do we need this behavior here?
227a7dea167SDimitry Andric     const Stmt *S = N->getStmtForDiagnostics();
2280b57cec5SDimitry Andric     if (!S) {
2290b57cec5SDimitry Andric       N = N->getFirstPred();
2300b57cec5SDimitry Andric       continue;
2310b57cec5SDimitry Andric     }
2320b57cec5SDimitry Andric 
233*0fca6ea1SDimitry Andric     if (isFoundInStmt(S, VD)) {
234*0fca6ea1SDimitry Andric       return false;
235*0fca6ea1SDimitry Andric     }
236*0fca6ea1SDimitry Andric 
237*0fca6ea1SDimitry Andric     if (const auto *SS = dyn_cast<SwitchStmt>(S)) {
238*0fca6ea1SDimitry Andric       if (const auto *CST = dyn_cast<CompoundStmt>(SS->getBody())) {
239*0fca6ea1SDimitry Andric         for (const Stmt *CB : CST->body()) {
240*0fca6ea1SDimitry Andric           if (isFoundInStmt(CB, VD))
2410b57cec5SDimitry Andric             return false;
2420b57cec5SDimitry Andric         }
2430b57cec5SDimitry Andric       }
244*0fca6ea1SDimitry Andric     }
245*0fca6ea1SDimitry Andric 
2460b57cec5SDimitry Andric     // Check the usage of the pass-by-ref function calls and adress-of operator
2470b57cec5SDimitry Andric     // on VD and reference initialized by VD.
2480b57cec5SDimitry Andric     ASTContext &ASTCtx =
2490b57cec5SDimitry Andric         N->getLocationContext()->getAnalysisDeclContext()->getASTContext();
250fe6060f1SDimitry Andric     // Case 3 and 4:
2510b57cec5SDimitry Andric     auto Match =
2520b57cec5SDimitry Andric         match(stmt(anyOf(callByRef(equalsNode(VD)), getAddrTo(equalsNode(VD)),
2530b57cec5SDimitry Andric                          assignedToRef(equalsNode(VD)))),
2540b57cec5SDimitry Andric               *S, ASTCtx);
2550b57cec5SDimitry Andric     if (!Match.empty())
2560b57cec5SDimitry Andric       return true;
2570b57cec5SDimitry Andric 
2580b57cec5SDimitry Andric     N = N->getFirstPred();
2590b57cec5SDimitry Andric   }
2605ffd83dbSDimitry Andric 
261fe6060f1SDimitry Andric   // Reference parameter and reference capture will not be found.
262fe6060f1SDimitry Andric   if (IsRefParamOrCapture)
2635ffd83dbSDimitry Andric     return false;
2645ffd83dbSDimitry Andric 
2650b57cec5SDimitry Andric   llvm_unreachable("Reached root without finding the declaration of VD");
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
2690b57cec5SDimitry Andric                             ExplodedNode *Pred, unsigned &maxStep) {
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   if (!isLoopStmt(LoopStmt))
2720b57cec5SDimitry Andric     return false;
2730b57cec5SDimitry Andric 
2740b57cec5SDimitry Andric   // TODO: Match the cases where the bound is not a concrete literal but an
2750b57cec5SDimitry Andric   // integer with known value
2760b57cec5SDimitry Andric   auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx);
2770b57cec5SDimitry Andric   if (Matches.empty())
2780b57cec5SDimitry Andric     return false;
2790b57cec5SDimitry Andric 
280fe6060f1SDimitry Andric   const auto *CounterVarRef = Matches[0].getNodeAs<DeclRefExpr>("initVarRef");
2810b57cec5SDimitry Andric   llvm::APInt BoundNum =
2820b57cec5SDimitry Andric       Matches[0].getNodeAs<IntegerLiteral>("boundNum")->getValue();
2830b57cec5SDimitry Andric   llvm::APInt InitNum =
2840b57cec5SDimitry Andric       Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
2850b57cec5SDimitry Andric   auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
2860b57cec5SDimitry Andric   if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
28781ad6265SDimitry Andric     InitNum = InitNum.zext(BoundNum.getBitWidth());
28881ad6265SDimitry Andric     BoundNum = BoundNum.zext(InitNum.getBitWidth());
2890b57cec5SDimitry Andric   }
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric   if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
2920b57cec5SDimitry Andric     maxStep = (BoundNum - InitNum + 1).abs().getZExtValue();
2930b57cec5SDimitry Andric   else
2940b57cec5SDimitry Andric     maxStep = (BoundNum - InitNum).abs().getZExtValue();
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric   // Check if the counter of the loop is not escaped before.
297fe6060f1SDimitry Andric   return !isPossiblyEscaped(Pred, CounterVarRef);
2980b57cec5SDimitry Andric }
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
3010b57cec5SDimitry Andric   const Stmt *S = nullptr;
3020b57cec5SDimitry Andric   while (!N->pred_empty()) {
3030b57cec5SDimitry Andric     if (N->succ_size() > 1)
3040b57cec5SDimitry Andric       return true;
3050b57cec5SDimitry Andric 
3060b57cec5SDimitry Andric     ProgramPoint P = N->getLocation();
307bdd1243dSDimitry Andric     if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
3080b57cec5SDimitry Andric       S = BE->getBlock()->getTerminatorStmt();
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric     if (S == LoopStmt)
3110b57cec5SDimitry Andric       return false;
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric     N = N->getFirstPred();
3140b57cec5SDimitry Andric   }
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric   llvm_unreachable("Reached root without encountering the previous step");
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric // updateLoopStack is called on every basic block, therefore it needs to be fast
3200b57cec5SDimitry Andric ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx,
3210b57cec5SDimitry Andric                                 ExplodedNode *Pred, unsigned maxVisitOnPath) {
3220b57cec5SDimitry Andric   auto State = Pred->getState();
3230b57cec5SDimitry Andric   auto LCtx = Pred->getLocationContext();
3240b57cec5SDimitry Andric 
3250b57cec5SDimitry Andric   if (!isLoopStmt(LoopStmt))
3260b57cec5SDimitry Andric     return State;
3270b57cec5SDimitry Andric 
3280b57cec5SDimitry Andric   auto LS = State->get<LoopStack>();
3290b57cec5SDimitry Andric   if (!LS.isEmpty() && LoopStmt == LS.getHead().getLoopStmt() &&
3300b57cec5SDimitry Andric       LCtx == LS.getHead().getLocationContext()) {
3310b57cec5SDimitry Andric     if (LS.getHead().isUnrolled() && madeNewBranch(Pred, LoopStmt)) {
3320b57cec5SDimitry Andric       State = State->set<LoopStack>(LS.getTail());
3330b57cec5SDimitry Andric       State = State->add<LoopStack>(
3340b57cec5SDimitry Andric           LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
3350b57cec5SDimitry Andric     }
3360b57cec5SDimitry Andric     return State;
3370b57cec5SDimitry Andric   }
3380b57cec5SDimitry Andric   unsigned maxStep;
3390b57cec5SDimitry Andric   if (!shouldCompletelyUnroll(LoopStmt, ASTCtx, Pred, maxStep)) {
3400b57cec5SDimitry Andric     State = State->add<LoopStack>(
3410b57cec5SDimitry Andric         LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
3420b57cec5SDimitry Andric     return State;
3430b57cec5SDimitry Andric   }
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   unsigned outerStep = (LS.isEmpty() ? 1 : LS.getHead().getMaxStep());
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric   unsigned innerMaxStep = maxStep * outerStep;
3480b57cec5SDimitry Andric   if (innerMaxStep > MAXIMUM_STEP_UNROLLED)
3490b57cec5SDimitry Andric     State = State->add<LoopStack>(
3500b57cec5SDimitry Andric         LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
3510b57cec5SDimitry Andric   else
3520b57cec5SDimitry Andric     State = State->add<LoopStack>(
3530b57cec5SDimitry Andric         LoopState::getUnrolled(LoopStmt, LCtx, innerMaxStep));
3540b57cec5SDimitry Andric   return State;
3550b57cec5SDimitry Andric }
3560b57cec5SDimitry Andric 
3570b57cec5SDimitry Andric bool isUnrolledState(ProgramStateRef State) {
3580b57cec5SDimitry Andric   auto LS = State->get<LoopStack>();
3590b57cec5SDimitry Andric   if (LS.isEmpty() || !LS.getHead().isUnrolled())
3600b57cec5SDimitry Andric     return false;
3610b57cec5SDimitry Andric   return true;
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric }
365