llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp

210 lines
7.9 KiB
C++

//===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// This file contains functions which are used to decide if a loop worth to be
/// unrolled. Moreover contains function which mark the CFGBlocks which belongs
/// to the unrolled loop and store them in ProgramState.
///
//===----------------------------------------------------------------------===//
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
#include "llvm/ADT/Statistic.h"
using namespace clang;
using namespace ento;
using namespace clang::ast_matchers;
#define DEBUG_TYPE "LoopUnrolling"
STATISTIC(NumTimesLoopUnrolled,
"The # of times a loop has got completely unrolled");
REGISTER_MAP_WITH_PROGRAMSTATE(UnrolledLoops, const Stmt *,
const FunctionDecl *)
namespace clang {
namespace ento {
static bool isLoopStmt(const Stmt *S) {
return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S));
}
static internal::Matcher<Stmt> simpleCondition(StringRef BindName) {
return binaryOperator(
anyOf(hasOperatorName("<"), hasOperatorName(">"), hasOperatorName("<="),
hasOperatorName(">="), hasOperatorName("!=")),
hasEitherOperand(ignoringParenImpCasts(
declRefExpr(to(varDecl(hasType(isInteger())).bind(BindName))))),
hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
}
static internal::Matcher<Stmt> changeIntBoundNode(StringRef NodeName) {
return anyOf(hasDescendant(unaryOperator(
anyOf(hasOperatorName("--"), hasOperatorName("++")),
hasUnaryOperand(ignoringParenImpCasts(
declRefExpr(to(varDecl(equalsBoundNode(NodeName)))))))),
hasDescendant(binaryOperator(
anyOf(hasOperatorName("="), hasOperatorName("+="),
hasOperatorName("/="), hasOperatorName("*="),
hasOperatorName("-=")),
hasLHS(ignoringParenImpCasts(
declRefExpr(to(varDecl(equalsBoundNode(NodeName)))))))));
}
static internal::Matcher<Stmt> callByRef(StringRef NodeName) {
return hasDescendant(callExpr(forEachArgumentWithParam(
declRefExpr(to(varDecl(equalsBoundNode(NodeName)))),
parmVarDecl(hasType(references(qualType(unless(isConstQualified()))))))));
}
static internal::Matcher<Stmt> assignedToRef(StringRef NodeName) {
return hasDescendant(varDecl(
allOf(hasType(referenceType()),
hasInitializer(
anyOf(initListExpr(has(
declRefExpr(to(varDecl(equalsBoundNode(NodeName)))))),
declRefExpr(to(varDecl(equalsBoundNode(NodeName)))))))));
}
static internal::Matcher<Stmt> getAddrTo(StringRef NodeName) {
return hasDescendant(unaryOperator(
hasOperatorName("&"),
hasUnaryOperand(declRefExpr(hasDeclaration(equalsBoundNode(NodeName))))));
}
static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
return anyOf(hasDescendant(gotoStmt()), hasDescendant(switchStmt()),
// 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.
changeIntBoundNode(NodeName), callByRef(NodeName),
getAddrTo(NodeName), assignedToRef(NodeName));
}
static internal::Matcher<Stmt> forLoopMatcher() {
return forStmt(
hasCondition(simpleCondition("initVarName")),
// Initialization should match the form: 'int i = 6' or 'i = 42'.
hasLoopInit(
anyOf(declStmt(hasSingleDecl(
varDecl(allOf(hasInitializer(integerLiteral()),
equalsBoundNode("initVarName"))))),
binaryOperator(hasLHS(declRefExpr(to(varDecl(
equalsBoundNode("initVarName"))))),
hasRHS(integerLiteral())))),
// Incrementation should be a simple increment or decrement
// operator call.
hasIncrement(unaryOperator(
anyOf(hasOperatorName("++"), hasOperatorName("--")),
hasUnaryOperand(declRefExpr(
to(varDecl(allOf(equalsBoundNode("initVarName"),
hasType(isInteger())))))))),
unless(hasBody(hasSuspiciousStmt("initVarName")))).bind("forLoop");
}
bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx) {
if (!isLoopStmt(LoopStmt))
return false;
// TODO: Match the cases where the bound is not a concrete literal but an
// integer with known value
auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx);
return !Matches.empty();
}
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();
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;
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;
}
ProgramStateRef markLoopAsUnrolled(const Stmt *Term, ProgramStateRef State,
const FunctionDecl *FD) {
if (State->contains<UnrolledLoops>(Term))
return State;
State = State->set<UnrolledLoops>(Term, FD);
++NumTimesLoopUnrolled;
return State;
}
}
}