xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// This file contains functions which are used to decide if a loop worth to be
10e5dd7070Spatrick /// unrolled. Moreover, these functions manages the stack of loop which is
11e5dd7070Spatrick /// tracked by the ProgramState.
12e5dd7070Spatrick ///
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick 
15e5dd7070Spatrick #include "clang/ASTMatchers/ASTMatchers.h"
16e5dd7070Spatrick #include "clang/ASTMatchers/ASTMatchFinder.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
20*12c85518Srobert #include <optional>
21e5dd7070Spatrick 
22e5dd7070Spatrick using namespace clang;
23e5dd7070Spatrick using namespace ento;
24e5dd7070Spatrick using namespace clang::ast_matchers;
25e5dd7070Spatrick 
26e5dd7070Spatrick static const int MAXIMUM_STEP_UNROLLED = 128;
27e5dd7070Spatrick 
28*12c85518Srobert namespace {
29e5dd7070Spatrick struct LoopState {
30e5dd7070Spatrick private:
31e5dd7070Spatrick   enum Kind { Normal, Unrolled } K;
32e5dd7070Spatrick   const Stmt *LoopStmt;
33e5dd7070Spatrick   const LocationContext *LCtx;
34e5dd7070Spatrick   unsigned maxStep;
LoopState__anon4532ab9a0111::LoopState35e5dd7070Spatrick   LoopState(Kind InK, const Stmt *S, const LocationContext *L, unsigned N)
36e5dd7070Spatrick       : K(InK), LoopStmt(S), LCtx(L), maxStep(N) {}
37e5dd7070Spatrick 
38e5dd7070Spatrick public:
getNormal__anon4532ab9a0111::LoopState39e5dd7070Spatrick   static LoopState getNormal(const Stmt *S, const LocationContext *L,
40e5dd7070Spatrick                              unsigned N) {
41e5dd7070Spatrick     return LoopState(Normal, S, L, N);
42e5dd7070Spatrick   }
getUnrolled__anon4532ab9a0111::LoopState43e5dd7070Spatrick   static LoopState getUnrolled(const Stmt *S, const LocationContext *L,
44e5dd7070Spatrick                                unsigned N) {
45e5dd7070Spatrick     return LoopState(Unrolled, S, L, N);
46e5dd7070Spatrick   }
isUnrolled__anon4532ab9a0111::LoopState47e5dd7070Spatrick   bool isUnrolled() const { return K == Unrolled; }
getMaxStep__anon4532ab9a0111::LoopState48e5dd7070Spatrick   unsigned getMaxStep() const { return maxStep; }
getLoopStmt__anon4532ab9a0111::LoopState49e5dd7070Spatrick   const Stmt *getLoopStmt() const { return LoopStmt; }
getLocationContext__anon4532ab9a0111::LoopState50e5dd7070Spatrick   const LocationContext *getLocationContext() const { return LCtx; }
operator ==__anon4532ab9a0111::LoopState51e5dd7070Spatrick   bool operator==(const LoopState &X) const {
52e5dd7070Spatrick     return K == X.K && LoopStmt == X.LoopStmt;
53e5dd7070Spatrick   }
Profile__anon4532ab9a0111::LoopState54e5dd7070Spatrick   void Profile(llvm::FoldingSetNodeID &ID) const {
55e5dd7070Spatrick     ID.AddInteger(K);
56e5dd7070Spatrick     ID.AddPointer(LoopStmt);
57e5dd7070Spatrick     ID.AddPointer(LCtx);
58e5dd7070Spatrick     ID.AddInteger(maxStep);
59e5dd7070Spatrick   }
60e5dd7070Spatrick };
61*12c85518Srobert } // namespace
62e5dd7070Spatrick 
63e5dd7070Spatrick // The tracked stack of loops. The stack indicates that which loops the
64e5dd7070Spatrick // simulated element contained by. The loops are marked depending if we decided
65e5dd7070Spatrick // to unroll them.
66e5dd7070Spatrick // TODO: The loop stack should not need to be in the program state since it is
67e5dd7070Spatrick // lexical in nature. Instead, the stack of loops should be tracked in the
68e5dd7070Spatrick // LocationContext.
69e5dd7070Spatrick REGISTER_LIST_WITH_PROGRAMSTATE(LoopStack, LoopState)
70e5dd7070Spatrick 
71e5dd7070Spatrick namespace clang {
72e5dd7070Spatrick namespace ento {
73e5dd7070Spatrick 
isLoopStmt(const Stmt * S)74e5dd7070Spatrick static bool isLoopStmt(const Stmt *S) {
75*12c85518Srobert   return isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(S);
76e5dd7070Spatrick }
77e5dd7070Spatrick 
processLoopEnd(const Stmt * LoopStmt,ProgramStateRef State)78e5dd7070Spatrick ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
79e5dd7070Spatrick   auto LS = State->get<LoopStack>();
80e5dd7070Spatrick   if (!LS.isEmpty() && LS.getHead().getLoopStmt() == LoopStmt)
81e5dd7070Spatrick     State = State->set<LoopStack>(LS.getTail());
82e5dd7070Spatrick   return State;
83e5dd7070Spatrick }
84e5dd7070Spatrick 
simpleCondition(StringRef BindName,StringRef RefName)85a9ac8606Spatrick static internal::Matcher<Stmt> simpleCondition(StringRef BindName,
86a9ac8606Spatrick                                                StringRef RefName) {
87a9ac8606Spatrick   return binaryOperator(
88a9ac8606Spatrick              anyOf(hasOperatorName("<"), hasOperatorName(">"),
89e5dd7070Spatrick                    hasOperatorName("<="), hasOperatorName(">="),
90e5dd7070Spatrick                    hasOperatorName("!=")),
91e5dd7070Spatrick              hasEitherOperand(ignoringParenImpCasts(
92a9ac8606Spatrick                  declRefExpr(to(varDecl(hasType(isInteger())).bind(BindName)))
93a9ac8606Spatrick                      .bind(RefName))),
94a9ac8606Spatrick              hasEitherOperand(
95a9ac8606Spatrick                  ignoringParenImpCasts(integerLiteral().bind("boundNum"))))
96e5dd7070Spatrick       .bind("conditionOperator");
97e5dd7070Spatrick }
98e5dd7070Spatrick 
99e5dd7070Spatrick static internal::Matcher<Stmt>
changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher)100e5dd7070Spatrick changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) {
101e5dd7070Spatrick   return anyOf(
102e5dd7070Spatrick       unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")),
103e5dd7070Spatrick                     hasUnaryOperand(ignoringParenImpCasts(
104e5dd7070Spatrick                         declRefExpr(to(varDecl(VarNodeMatcher)))))),
105e5dd7070Spatrick       binaryOperator(isAssignmentOperator(),
106e5dd7070Spatrick                      hasLHS(ignoringParenImpCasts(
107e5dd7070Spatrick                          declRefExpr(to(varDecl(VarNodeMatcher)))))));
108e5dd7070Spatrick }
109e5dd7070Spatrick 
110e5dd7070Spatrick static internal::Matcher<Stmt>
callByRef(internal::Matcher<Decl> VarNodeMatcher)111e5dd7070Spatrick callByRef(internal::Matcher<Decl> VarNodeMatcher) {
112e5dd7070Spatrick   return callExpr(forEachArgumentWithParam(
113e5dd7070Spatrick       declRefExpr(to(varDecl(VarNodeMatcher))),
114e5dd7070Spatrick       parmVarDecl(hasType(references(qualType(unless(isConstQualified())))))));
115e5dd7070Spatrick }
116e5dd7070Spatrick 
117e5dd7070Spatrick static internal::Matcher<Stmt>
assignedToRef(internal::Matcher<Decl> VarNodeMatcher)118e5dd7070Spatrick assignedToRef(internal::Matcher<Decl> VarNodeMatcher) {
119e5dd7070Spatrick   return declStmt(hasDescendant(varDecl(
120e5dd7070Spatrick       allOf(hasType(referenceType()),
121e5dd7070Spatrick             hasInitializer(anyOf(
122e5dd7070Spatrick                 initListExpr(has(declRefExpr(to(varDecl(VarNodeMatcher))))),
123e5dd7070Spatrick                 declRefExpr(to(varDecl(VarNodeMatcher)))))))));
124e5dd7070Spatrick }
125e5dd7070Spatrick 
126e5dd7070Spatrick static internal::Matcher<Stmt>
getAddrTo(internal::Matcher<Decl> VarNodeMatcher)127e5dd7070Spatrick getAddrTo(internal::Matcher<Decl> VarNodeMatcher) {
128e5dd7070Spatrick   return unaryOperator(
129e5dd7070Spatrick       hasOperatorName("&"),
130e5dd7070Spatrick       hasUnaryOperand(declRefExpr(hasDeclaration(VarNodeMatcher))));
131e5dd7070Spatrick }
132e5dd7070Spatrick 
hasSuspiciousStmt(StringRef NodeName)133e5dd7070Spatrick static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
134e5dd7070Spatrick   return hasDescendant(stmt(
135e5dd7070Spatrick       anyOf(gotoStmt(), switchStmt(), returnStmt(),
136e5dd7070Spatrick             // Escaping and not known mutation of the loop counter is handled
137e5dd7070Spatrick             // by exclusion of assigning and address-of operators and
138e5dd7070Spatrick             // pass-by-ref function calls on the loop counter from the body.
139ec727ea7Spatrick             changeIntBoundNode(equalsBoundNode(std::string(NodeName))),
140ec727ea7Spatrick             callByRef(equalsBoundNode(std::string(NodeName))),
141ec727ea7Spatrick             getAddrTo(equalsBoundNode(std::string(NodeName))),
142ec727ea7Spatrick             assignedToRef(equalsBoundNode(std::string(NodeName))))));
143e5dd7070Spatrick }
144e5dd7070Spatrick 
forLoopMatcher()145e5dd7070Spatrick static internal::Matcher<Stmt> forLoopMatcher() {
146e5dd7070Spatrick   return forStmt(
147a9ac8606Spatrick              hasCondition(simpleCondition("initVarName", "initVarRef")),
148e5dd7070Spatrick              // Initialization should match the form: 'int i = 6' or 'i = 42'.
149e5dd7070Spatrick              hasLoopInit(
150e5dd7070Spatrick                  anyOf(declStmt(hasSingleDecl(
151e5dd7070Spatrick                            varDecl(allOf(hasInitializer(ignoringParenImpCasts(
152e5dd7070Spatrick                                              integerLiteral().bind("initNum"))),
153e5dd7070Spatrick                                          equalsBoundNode("initVarName"))))),
154e5dd7070Spatrick                        binaryOperator(hasLHS(declRefExpr(to(varDecl(
155e5dd7070Spatrick                                           equalsBoundNode("initVarName"))))),
156e5dd7070Spatrick                                       hasRHS(ignoringParenImpCasts(
157e5dd7070Spatrick                                           integerLiteral().bind("initNum")))))),
158e5dd7070Spatrick              // Incrementation should be a simple increment or decrement
159e5dd7070Spatrick              // operator call.
160e5dd7070Spatrick              hasIncrement(unaryOperator(
161e5dd7070Spatrick                  anyOf(hasOperatorName("++"), hasOperatorName("--")),
162e5dd7070Spatrick                  hasUnaryOperand(declRefExpr(
163e5dd7070Spatrick                      to(varDecl(allOf(equalsBoundNode("initVarName"),
164e5dd7070Spatrick                                       hasType(isInteger())))))))),
165a9ac8606Spatrick              unless(hasBody(hasSuspiciousStmt("initVarName"))))
166a9ac8606Spatrick       .bind("forLoop");
167e5dd7070Spatrick }
168e5dd7070Spatrick 
isCapturedByReference(ExplodedNode * N,const DeclRefExpr * DR)169a9ac8606Spatrick static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) {
170a9ac8606Spatrick 
171a9ac8606Spatrick   // Get the lambda CXXRecordDecl
172a9ac8606Spatrick   assert(DR->refersToEnclosingVariableOrCapture());
173a9ac8606Spatrick   const LocationContext *LocCtxt = N->getLocationContext();
174a9ac8606Spatrick   const Decl *D = LocCtxt->getDecl();
175a9ac8606Spatrick   const auto *MD = cast<CXXMethodDecl>(D);
176a9ac8606Spatrick   assert(MD && MD->getParent()->isLambda() &&
177a9ac8606Spatrick          "Captured variable should only be seen while evaluating a lambda");
178a9ac8606Spatrick   const CXXRecordDecl *LambdaCXXRec = MD->getParent();
179a9ac8606Spatrick 
180a9ac8606Spatrick   // Lookup the fields of the lambda
181*12c85518Srobert   llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
182a9ac8606Spatrick   FieldDecl *LambdaThisCaptureField;
183a9ac8606Spatrick   LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
184a9ac8606Spatrick 
185a9ac8606Spatrick   // Check if the counter is captured by reference
186a9ac8606Spatrick   const VarDecl *VD = cast<VarDecl>(DR->getDecl()->getCanonicalDecl());
187a9ac8606Spatrick   assert(VD);
188a9ac8606Spatrick   const FieldDecl *FD = LambdaCaptureFields[VD];
189a9ac8606Spatrick   assert(FD && "Captured variable without a corresponding field");
190a9ac8606Spatrick   return FD->getType()->isReferenceType();
191a9ac8606Spatrick }
192a9ac8606Spatrick 
193a9ac8606Spatrick // A loop counter is considered escaped if:
194a9ac8606Spatrick // case 1: It is a global variable.
195a9ac8606Spatrick // case 2: It is a reference parameter or a reference capture.
196a9ac8606Spatrick // case 3: It is assigned to a non-const reference variable or parameter.
197a9ac8606Spatrick // case 4: Has its address taken.
isPossiblyEscaped(ExplodedNode * N,const DeclRefExpr * DR)198a9ac8606Spatrick static bool isPossiblyEscaped(ExplodedNode *N, const DeclRefExpr *DR) {
199a9ac8606Spatrick   const VarDecl *VD = cast<VarDecl>(DR->getDecl()->getCanonicalDecl());
200a9ac8606Spatrick   assert(VD);
201a9ac8606Spatrick   // Case 1:
202e5dd7070Spatrick   if (VD->hasGlobalStorage())
203e5dd7070Spatrick     return true;
204e5dd7070Spatrick 
205a9ac8606Spatrick   const bool IsRefParamOrCapture =
206a9ac8606Spatrick       isa<ParmVarDecl>(VD) || DR->refersToEnclosingVariableOrCapture();
207a9ac8606Spatrick   // Case 2:
208a9ac8606Spatrick   if ((DR->refersToEnclosingVariableOrCapture() &&
209a9ac8606Spatrick        isCapturedByReference(N, DR)) ||
210a9ac8606Spatrick       (IsRefParamOrCapture && VD->getType()->isReferenceType()))
211ec727ea7Spatrick     return true;
212ec727ea7Spatrick 
213e5dd7070Spatrick   while (!N->pred_empty()) {
214e5dd7070Spatrick     // FIXME: getStmtForDiagnostics() does nasty things in order to provide
215e5dd7070Spatrick     // a valid statement for body farms, do we need this behavior here?
216e5dd7070Spatrick     const Stmt *S = N->getStmtForDiagnostics();
217e5dd7070Spatrick     if (!S) {
218e5dd7070Spatrick       N = N->getFirstPred();
219e5dd7070Spatrick       continue;
220e5dd7070Spatrick     }
221e5dd7070Spatrick 
222e5dd7070Spatrick     if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
223e5dd7070Spatrick       for (const Decl *D : DS->decls()) {
224e5dd7070Spatrick         // Once we reach the declaration of the VD we can return.
225e5dd7070Spatrick         if (D->getCanonicalDecl() == VD)
226e5dd7070Spatrick           return false;
227e5dd7070Spatrick       }
228e5dd7070Spatrick     }
229e5dd7070Spatrick     // Check the usage of the pass-by-ref function calls and adress-of operator
230e5dd7070Spatrick     // on VD and reference initialized by VD.
231e5dd7070Spatrick     ASTContext &ASTCtx =
232e5dd7070Spatrick         N->getLocationContext()->getAnalysisDeclContext()->getASTContext();
233a9ac8606Spatrick     // Case 3 and 4:
234e5dd7070Spatrick     auto Match =
235e5dd7070Spatrick         match(stmt(anyOf(callByRef(equalsNode(VD)), getAddrTo(equalsNode(VD)),
236e5dd7070Spatrick                          assignedToRef(equalsNode(VD)))),
237e5dd7070Spatrick               *S, ASTCtx);
238e5dd7070Spatrick     if (!Match.empty())
239e5dd7070Spatrick       return true;
240e5dd7070Spatrick 
241e5dd7070Spatrick     N = N->getFirstPred();
242e5dd7070Spatrick   }
243ec727ea7Spatrick 
244a9ac8606Spatrick   // Reference parameter and reference capture will not be found.
245a9ac8606Spatrick   if (IsRefParamOrCapture)
246ec727ea7Spatrick     return false;
247ec727ea7Spatrick 
248e5dd7070Spatrick   llvm_unreachable("Reached root without finding the declaration of VD");
249e5dd7070Spatrick }
250e5dd7070Spatrick 
shouldCompletelyUnroll(const Stmt * LoopStmt,ASTContext & ASTCtx,ExplodedNode * Pred,unsigned & maxStep)251e5dd7070Spatrick bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
252e5dd7070Spatrick                             ExplodedNode *Pred, unsigned &maxStep) {
253e5dd7070Spatrick 
254e5dd7070Spatrick   if (!isLoopStmt(LoopStmt))
255e5dd7070Spatrick     return false;
256e5dd7070Spatrick 
257e5dd7070Spatrick   // TODO: Match the cases where the bound is not a concrete literal but an
258e5dd7070Spatrick   // integer with known value
259e5dd7070Spatrick   auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx);
260e5dd7070Spatrick   if (Matches.empty())
261e5dd7070Spatrick     return false;
262e5dd7070Spatrick 
263a9ac8606Spatrick   const auto *CounterVarRef = Matches[0].getNodeAs<DeclRefExpr>("initVarRef");
264e5dd7070Spatrick   llvm::APInt BoundNum =
265e5dd7070Spatrick       Matches[0].getNodeAs<IntegerLiteral>("boundNum")->getValue();
266e5dd7070Spatrick   llvm::APInt InitNum =
267e5dd7070Spatrick       Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
268e5dd7070Spatrick   auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
269e5dd7070Spatrick   if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
270*12c85518Srobert     InitNum = InitNum.zext(BoundNum.getBitWidth());
271*12c85518Srobert     BoundNum = BoundNum.zext(InitNum.getBitWidth());
272e5dd7070Spatrick   }
273e5dd7070Spatrick 
274e5dd7070Spatrick   if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
275e5dd7070Spatrick     maxStep = (BoundNum - InitNum + 1).abs().getZExtValue();
276e5dd7070Spatrick   else
277e5dd7070Spatrick     maxStep = (BoundNum - InitNum).abs().getZExtValue();
278e5dd7070Spatrick 
279e5dd7070Spatrick   // Check if the counter of the loop is not escaped before.
280a9ac8606Spatrick   return !isPossiblyEscaped(Pred, CounterVarRef);
281e5dd7070Spatrick }
282e5dd7070Spatrick 
madeNewBranch(ExplodedNode * N,const Stmt * LoopStmt)283e5dd7070Spatrick bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
284e5dd7070Spatrick   const Stmt *S = nullptr;
285e5dd7070Spatrick   while (!N->pred_empty()) {
286e5dd7070Spatrick     if (N->succ_size() > 1)
287e5dd7070Spatrick       return true;
288e5dd7070Spatrick 
289e5dd7070Spatrick     ProgramPoint P = N->getLocation();
290*12c85518Srobert     if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
291e5dd7070Spatrick       S = BE->getBlock()->getTerminatorStmt();
292e5dd7070Spatrick 
293e5dd7070Spatrick     if (S == LoopStmt)
294e5dd7070Spatrick       return false;
295e5dd7070Spatrick 
296e5dd7070Spatrick     N = N->getFirstPred();
297e5dd7070Spatrick   }
298e5dd7070Spatrick 
299e5dd7070Spatrick   llvm_unreachable("Reached root without encountering the previous step");
300e5dd7070Spatrick }
301e5dd7070Spatrick 
302e5dd7070Spatrick // updateLoopStack is called on every basic block, therefore it needs to be fast
updateLoopStack(const Stmt * LoopStmt,ASTContext & ASTCtx,ExplodedNode * Pred,unsigned maxVisitOnPath)303e5dd7070Spatrick ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx,
304e5dd7070Spatrick                                 ExplodedNode *Pred, unsigned maxVisitOnPath) {
305e5dd7070Spatrick   auto State = Pred->getState();
306e5dd7070Spatrick   auto LCtx = Pred->getLocationContext();
307e5dd7070Spatrick 
308e5dd7070Spatrick   if (!isLoopStmt(LoopStmt))
309e5dd7070Spatrick     return State;
310e5dd7070Spatrick 
311e5dd7070Spatrick   auto LS = State->get<LoopStack>();
312e5dd7070Spatrick   if (!LS.isEmpty() && LoopStmt == LS.getHead().getLoopStmt() &&
313e5dd7070Spatrick       LCtx == LS.getHead().getLocationContext()) {
314e5dd7070Spatrick     if (LS.getHead().isUnrolled() && madeNewBranch(Pred, LoopStmt)) {
315e5dd7070Spatrick       State = State->set<LoopStack>(LS.getTail());
316e5dd7070Spatrick       State = State->add<LoopStack>(
317e5dd7070Spatrick           LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
318e5dd7070Spatrick     }
319e5dd7070Spatrick     return State;
320e5dd7070Spatrick   }
321e5dd7070Spatrick   unsigned maxStep;
322e5dd7070Spatrick   if (!shouldCompletelyUnroll(LoopStmt, ASTCtx, Pred, maxStep)) {
323e5dd7070Spatrick     State = State->add<LoopStack>(
324e5dd7070Spatrick         LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
325e5dd7070Spatrick     return State;
326e5dd7070Spatrick   }
327e5dd7070Spatrick 
328e5dd7070Spatrick   unsigned outerStep = (LS.isEmpty() ? 1 : LS.getHead().getMaxStep());
329e5dd7070Spatrick 
330e5dd7070Spatrick   unsigned innerMaxStep = maxStep * outerStep;
331e5dd7070Spatrick   if (innerMaxStep > MAXIMUM_STEP_UNROLLED)
332e5dd7070Spatrick     State = State->add<LoopStack>(
333e5dd7070Spatrick         LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
334e5dd7070Spatrick   else
335e5dd7070Spatrick     State = State->add<LoopStack>(
336e5dd7070Spatrick         LoopState::getUnrolled(LoopStmt, LCtx, innerMaxStep));
337e5dd7070Spatrick   return State;
338e5dd7070Spatrick }
339e5dd7070Spatrick 
isUnrolledState(ProgramStateRef State)340e5dd7070Spatrick bool isUnrolledState(ProgramStateRef State) {
341e5dd7070Spatrick   auto LS = State->get<LoopStack>();
342e5dd7070Spatrick   if (LS.isEmpty() || !LS.getHead().isUnrolled())
343e5dd7070Spatrick     return false;
344e5dd7070Spatrick   return true;
345e5dd7070Spatrick }
346e5dd7070Spatrick }
347e5dd7070Spatrick }
348