31#include "llvm/Support/ErrorHandling.h"
32#include "llvm/Support/FormatVariadic.h"
33#include "llvm/Support/TimeProfiler.h"
43#define DEBUG_TYPE "CoreEngine"
49 "The # of times we reached the max number of steps.");
50STAT_COUNTER(NumPathsExplored,
"The # of paths explored by the analyzer.");
71 llvm_unreachable(
"Unknown AnalyzerOptions::ExplorationStrategyKind");
78 BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
81 WList->setBlockCounter(
C);
83 CTUWList->setBlockCounter(
C);
90 assert(!G.getRoot() &&
"empty graph must not have a root node");
95 assert(Entry->
empty() &&
"Entry block must be empty.");
97 assert(Entry->
succ_size() == 1 &&
"Entry block must have 1 successor.");
111 setBlockCounter(BCounterFactory.GetEmptyCounter());
114 InitState = ExprEng.getInitialState(SF);
117 ExplodedNode *Node = G.getNode(StartLoc, InitState,
false, &IsNew);
119 G.designateAsRoot(Node);
121 ExprEng.setCurrStackFrameAndBlock(Node->
getStackFrame(), Succ);
124 ExprEng.processBeginOfFunction(Node, DstBegin, StartLoc);
130 bool UnlimitedSteps = MaxSteps == 0;
134 const unsigned PreReservationCap = 4000000;
136 G.reserve(std::min(MaxSteps, PreReservationCap));
138 auto ProcessWList = [
this, UnlimitedSteps](
unsigned MaxSteps) {
139 unsigned Steps = MaxSteps;
140 while (WList->hasWork()) {
141 if (!UnlimitedSteps) {
143 NumReachedMaxSteps++;
161 return MaxSteps - Steps;
163 const unsigned STUSteps = ProcessWList(MaxSteps);
164 ExploredAllSTUPaths = !WList->hasWork();
167 NumSTUSteps += STUSteps;
168 const unsigned MinCTUSteps =
169 this->ExprEng.getAnalysisManager().options.CTUMaxNodesMin;
171 this->ExprEng.getAnalysisManager().options.CTUMaxNodesPercentage;
172 unsigned MaxCTUSteps = std::max(STUSteps * Pct / 100, MinCTUSteps);
174 WList = std::move(CTUWList);
175 const unsigned CTUSteps = ProcessWList(MaxCTUSteps);
176 NumCTUSteps += CTUSteps;
179 ExprEng.processEndWorklist();
180 return WList->hasWork();
184 if (llvm::timeTraceProfilerEnabled()) {
185 return llvm::formatv(
"dispatchWorkItem {0}",
195 assert(llvm::timeTraceProfilerEnabled());
196 std::string Detail =
"";
198 if (
const Stmt *S = SP->getStmt())
199 Detail = S->getStmtClassName();
201 auto SLoc =
Loc.getSourceLocation();
203 return llvm::TimeTraceMetadata{std::move(Detail),
""};
208 auto Line = SM.getPresumedLineNumber(*SLoc);
209 auto Fname = SM.getFilename(*SLoc);
210 return llvm::TimeTraceMetadata{std::move(Detail), Fname.str(),
211 static_cast<int>(
Line)};
226 ExprEng.resetCurrStackFrameAndBlock();
239 assert(
false &&
"BlockExit location never occur in forward analysis.");
247 ExprEng.processCallExit(Pred);
252 "Assume epsilon has exactly one predecessor by construction");
284 return "Virtual base initialization skipped because "
285 "it has already been handled by the most derived class";
296 if (Blk == &ExitBlk) {
297 assert(ExitBlk.
empty() &&
"EXIT block cannot contain Stmts.");
303 if (std::optional<CFGStmt> LastStmt = LastElement.
getAs<
CFGStmt>()) {
304 RS = dyn_cast<ReturnStmt>(LastStmt->getStmt());
305 }
else if (std::optional<CFGAutomaticObjDtor> AutoDtor =
306 LastElement.
getAs<CFGAutomaticObjDtor>()) {
307 RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt());
308 }
else if (std::optional<CFGScopeMarker> ScopeMarker =
309 LastElement.
getAs<CFGScopeMarker>()) {
310 RS = dyn_cast<ReturnStmt>(ScopeMarker->getTriggerStmt());
314 ExplodedNodeSet CheckerNodes;
316 ExprEng.runCheckersForBlockEntrance(BE, Pred, CheckerNodes);
319 for (ExplodedNode *P : CheckerNodes) {
320 ExprEng.processEndOfFunction(P, RS);
329 ExplodedNode *Processed = ExprEng.processCFGBlockEntrance(BE, Pred);
331 ExplodedNodeSet CheckerNodes;
334 ExprEng.runCheckersForBlockEntrance(BE, Processed, CheckerNodes);
340void CoreEngine::HandleBlockEntrance(
const BlockEntrance &L,
345 BlockCounter Counter = WList->getBlockCounter();
346 Counter = BCounterFactory.IncrementCount(Counter, SF, BlockId);
347 setBlockCounter(Counter);
352 ExprEng.processCFGElement(*E, Pred, 0);
354 HandleBlockExit(L.
getBlock(), Pred);
357void CoreEngine::HandleBlockExit(
const CFGBlock * B,
ExplodedNode *Pred) {
361 switch (Term->getStmtClass()) {
363 llvm_unreachable(
"Analysis for this terminator not implemented.");
365 case Stmt::CXXBindTemporaryExprClass:
366 HandleCleanupTemporaryBranch(
371 case Stmt::DeclStmtClass:
375 case Stmt::BinaryOperatorClass:
379 case Stmt::BinaryConditionalOperatorClass:
380 case Stmt::ConditionalOperatorClass:
388 case Stmt::ChooseExprClass:
392 case Stmt::CXXTryStmtClass:
395 for (
const CFGBlock *Succ : B->
succs()) {
398 if (ExplodedNode *N =
makeNode(BE, Pred->State, Pred))
404 case Stmt::DoStmtClass:
405 HandleBranch(
cast<DoStmt>(Term)->getCond(), Term, B, Pred);
408 case Stmt::CXXForRangeStmtClass:
412 case Stmt::ForStmtClass:
416 case Stmt::SEHLeaveStmtClass:
417 case Stmt::ContinueStmtClass:
418 case Stmt::BreakStmtClass:
419 case Stmt::GotoStmtClass:
422 case Stmt::IfStmtClass:
423 HandleBranch(
cast<IfStmt>(Term)->getCond(), Term, B, Pred);
426 case Stmt::IndirectGotoStmtClass: {
430 ExprEng.processIndirectGoto(Dst,
437 case Stmt::ObjCForCollectionStmtClass:
448 HandleBranch(Term, Term, B, Pred);
451 case Stmt::SwitchStmtClass: {
459 case Stmt::WhileStmtClass:
463 case Stmt::GCCAsmStmtClass:
464 assert(
cast<GCCAsmStmt>(Term)->isAsmGoto() &&
"Encountered GCCAsmStmt without labels");
471 HandleVirtualBaseBranch(B, Pred);
476 "Blocks with no terminator should have at most 1 successor.");
479 if (ExplodedNode *N =
makeNode(BE, Pred->State, Pred))
483void CoreEngine::HandleCallEnter(
const CallEnter &CE,
ExplodedNode *Pred) {
485 ExprEng.processCallEnter(CE, Pred);
488void CoreEngine::HandleBranch(
const Stmt *Cond,
const Stmt *Term,
492 ExprEng.processBranch(Cond, Pred, Dst, *(B->
succ_begin()),
494 getCompletedIterationCount(B, Pred));
499void CoreEngine::HandleCleanupTemporaryBranch(
const CXXBindTemporaryExpr *BTE,
504 ExprEng.processCleanupTemporaryBranch(BTE, Pred, Dst, *(B->
succ_begin()),
510void CoreEngine::HandleStaticInit(
const DeclStmt *DS,
const CFGBlock *B,
514 ExprEng.processStaticInitializer(DS, Pred, Dst, *(B->
succ_begin()),
520void CoreEngine::HandlePostStmt(
const CFGBlock *B,
unsigned StmtIdx,
526 while (StmtIdx < B->size() &&
531 if (StmtIdx == B->
size())
532 HandleBlockExit(B, Pred);
535 ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx);
539void CoreEngine::HandleVirtualBaseBranch(
const CFGBlock *B,
542 if (
const auto *CallerCtor =
543 dyn_cast_or_null<CXXConstructExpr>(SF->
getCallSite())) {
544 switch (CallerCtor->getConstructionKind()) {
548 HandleBlockEdge(Loc, Pred);
559 HandleBlockEdge(Loc, Pred);
564 bool MarkAsSink)
const {
565 bool IsPO = State->isPosteriorlyOverconstrained();
571 return (IsNew && !IsPO) ? N :
nullptr;
583 WList->enqueue(N,
Block, Idx);
592 WList->enqueue(N,
Block, Idx + 1);
597 WList->enqueue(N,
Block, Idx);
602 WList->enqueue(N,
Block, Idx+1);
613 WList->enqueue(N,
Block, Idx+1);
620 WList->enqueue(Succ,
Block, Idx+1);
623std::optional<unsigned>
624CoreEngine::getCompletedIterationCount(
const CFGBlock *B,
632 assert(BlockCount >= 1 &&
633 "Block count of currently analyzed block must be >= 1");
634 return BlockCount - 1;
647 for (
const auto I :
Set)
653 for (
const auto I :
Set)
666 WList->enqueue(Succ);
669 G.addEndOfPath(Node);
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static std::unique_ptr< WorkList > generateWorkList(AnalyzerOptions &Opts)
ALWAYS_ENABLED_STATISTIC(NumReachedMaxSteps, "The # of times we reached the max number of steps.")
static std::string timeTraceScopeName(const ProgramPoint &Loc)
static llvm::TimeTraceMetadata timeTraceMetadata(const ExplodedNode *Pred, const ProgramPoint &Loc)
static Decl::Kind getKind(const Decl *D)
#define STAT_COUNTER(VARNAME, DESC)
Defines the clang::Expr interface and subclasses for C++ expressions.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
SourceManager & getSourceManager()
ASTContext & getASTContext() const
Stores options for the analyzer from the command line.
ExplorationStrategyKind getExplorationStrategy() const
const CFGBlock * getSrc() const
const CFGBlock * getDst() const
std::optional< CFGElement > getFirstElement() const
const CFGBlock * getBlock() const
Represents a single basic block in a source-level CFG.
CFGTerminator getTerminator() const
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
unsigned getBlockID() const
unsigned succ_size() const
Represents a top-level expression in a basic block.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
std::optional< T > getAs() const
Convert to the specified CFGElement type, returning std::nullopt if this CFGElement is not of the des...
const Stmt * getStmt() const
bool isVirtualBaseBranch() const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
Represents a point when we begin processing an inlined call.
const CFGBlock * getEntry() const
Returns the entry block in the CFG for the entered function.
Represents a point when we start the call exit sequence (for inlined call).
Represents a point when we finish the call exit sequence (for inlined call).
This is a meta program point, which should be skipped by all the diagnostic reasoning etc.
Represents a point when the lifetime of an automatic object ends.
Represents a point when we exit a loop.
Represents a program point just after an implicit call event.
static StringRef getProgramPointKindName(Kind K)
ProgramPoint withTag(const ProgramPointTag *tag) const
Create a new ProgramPoint object that is the same as the original except for using the specified tag ...
const StackFrame * getStackFrame() const
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
It represents a stack frame of the call stack.
LLVM_ATTRIBUTE_RETURNS_NONNULL AnalysisDeclContext * getAnalysisDeclContext() const
const Expr * getCallSite() const
const Decl * getDecl() const
const StackFrame * getParent() const
It might return null.
Stmt - This represents one statement.
An abstract data type used to count the number of times a given block has been visited along a path a...
unsigned getNumVisited(const StackFrame *CallSite, unsigned BlockID) const
CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts)
Construct a CoreEngine object to analyze the provided CFG.
DataTag::Factory & getDataTags()
void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx)
Enqueue a single node created as a result of statement processing.
void dispatchWorkItem(ExplodedNode *Pred, ProgramPoint Loc, const WorkListUnit &WU)
Dispatch the work list item based on the given location information.
void enqueueStmtNodes(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx)
Enqueue nodes that were created as a result of processing a statement onto the work list.
bool ExecuteWorkList(const StackFrame *SF, unsigned Steps, ProgramStateRef InitState)
ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS)
enqueue the nodes corresponding to the end of function onto the end of path / work list.
ExplodedNode * makeNode(const ProgramPoint &Loc, ProgramStateRef State, ExplodedNode *Pred, bool MarkAsSink=false) const
void enqueue(ExplodedNodeSet &Set)
Enqueue the given set of nodes onto the work list.
ExplodedNodeSet is a set of ExplodedNode * elements with the invariant that its elements cannot be nu...
const ProgramStateRef & getState() const
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
void addPredecessor(ExplodedNode *V, ExplodedGraph &G)
addPredeccessor - Adds a predecessor to the current node, and in tandem add this node as a successor ...
bool hasSinglePred() const
ExplodedNode * getFirstPred()
const StackFrame * getStackFrame() const
void setCurrStackFrameAndBlock(const StackFrame *SF, const CFGBlock *B)
void markVisitedBasicBlock(unsigned ID, const Decl *D, unsigned TotalIDs)
While alive, includes the current analysis stack in a crash trace.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
ExplodedNode * getNode() const
Returns the node associated with the worklist unit.
unsigned getIndex() const
Return the index within the CFGBlock for the worklist unit.
const CFGBlock * getBlock() const
Returns the CFGblock associated with the worklist unit.
BlockCounter getBlockCounter() const
Returns the block counter map associated with the worklist unit.
static std::unique_ptr< WorkList > makeUnexploredFirstPriorityLocationQueue()
static std::unique_ptr< WorkList > makeUnexploredFirstPriorityQueue()
static std::unique_ptr< WorkList > makeBFSBlockDFSContents()
static std::unique_ptr< WorkList > makeBFS()
static std::unique_ptr< WorkList > makeDFS()
static std::unique_ptr< WorkList > makeUnexploredFirst()
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
U cast(CodeGen::Address addr)
@ UnexploredFirstLocationQueue