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