148675466SDimitry Andric //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===//
2ec2b103cSEd Schouten //
322989816SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422989816SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
522989816SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ec2b103cSEd Schouten //
7ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
8ec2b103cSEd Schouten //
9ec2b103cSEd Schouten // This file defines the template classes ExplodedNode and ExplodedGraph,
10ec2b103cSEd Schouten // which represent a path-sensitive, intra-procedural "exploded graph."
11ec2b103cSEd Schouten //
12ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
13ec2b103cSEd Schouten
14bca07a45SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
1548675466SDimitry Andric #include "clang/AST/Expr.h"
1648675466SDimitry Andric #include "clang/AST/ExprObjC.h"
17809500fcSDimitry Andric #include "clang/AST/ParentMap.h"
18809500fcSDimitry Andric #include "clang/AST/Stmt.h"
19519fc96cSDimitry Andric #include "clang/Analysis/CFGStmtMap.h"
2048675466SDimitry Andric #include "clang/Analysis/ProgramPoint.h"
2148675466SDimitry Andric #include "clang/Analysis/Support/BumpVector.h"
2248675466SDimitry Andric #include "clang/Basic/LLVM.h"
2356d91b49SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2436981b17SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2548675466SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
26809500fcSDimitry Andric #include "llvm/ADT/DenseSet.h"
2748675466SDimitry Andric #include "llvm/ADT/FoldingSet.h"
2848675466SDimitry Andric #include "llvm/ADT/PointerUnion.h"
29ec2b103cSEd Schouten #include "llvm/ADT/SmallVector.h"
3048675466SDimitry Andric #include "llvm/Support/Casting.h"
3148675466SDimitry Andric #include <cassert>
3248675466SDimitry Andric #include <memory>
33e3b55780SDimitry Andric #include <optional>
34ec2b103cSEd Schouten
35ec2b103cSEd Schouten using namespace clang;
36bca07a45SDimitry Andric using namespace ento;
37ec2b103cSEd Schouten
38ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
39bca07a45SDimitry Andric // Cleanup.
40bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
41bca07a45SDimitry Andric
4248675466SDimitry Andric ExplodedGraph::ExplodedGraph() = default;
43dbe13110SDimitry Andric
4448675466SDimitry Andric ExplodedGraph::~ExplodedGraph() = default;
45bca07a45SDimitry Andric
46bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
47bca07a45SDimitry Andric // Node reclamation.
48bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
49bca07a45SDimitry Andric
isInterestingLValueExpr(const Expr * Ex)50809500fcSDimitry Andric bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
51809500fcSDimitry Andric if (!Ex->isLValue())
52809500fcSDimitry Andric return false;
53c0981da4SDimitry Andric return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
54809500fcSDimitry Andric }
55809500fcSDimitry Andric
shouldCollect(const ExplodedNode * node)56dbe13110SDimitry Andric bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
57809500fcSDimitry Andric // First, we only consider nodes for reclamation of the following
58809500fcSDimitry Andric // conditions apply:
59bca07a45SDimitry Andric //
60bca07a45SDimitry Andric // (1) 1 predecessor (that has one successor)
61bca07a45SDimitry Andric // (2) 1 successor (that has one predecessor)
62809500fcSDimitry Andric //
63809500fcSDimitry Andric // If a node has no successor it is on the "frontier", while a node
64809500fcSDimitry Andric // with no predecessor is a root.
65809500fcSDimitry Andric //
66809500fcSDimitry Andric // After these prerequisites, we discard all "filler" nodes that
67809500fcSDimitry Andric // are used only for intermediate processing, and are not essential
68809500fcSDimitry Andric // for analyzer history:
69809500fcSDimitry Andric //
70809500fcSDimitry Andric // (a) PreStmtPurgeDeadSymbols
71809500fcSDimitry Andric //
72809500fcSDimitry Andric // We then discard all other nodes where *all* of the following conditions
73809500fcSDimitry Andric // apply:
74809500fcSDimitry Andric //
7513cc256eSDimitry Andric // (3) The ProgramPoint is for a PostStmt, but not a PostStore.
76bca07a45SDimitry Andric // (4) There is no 'tag' for the ProgramPoint.
77bca07a45SDimitry Andric // (5) The 'store' is the same as the predecessor.
78bca07a45SDimitry Andric // (6) The 'GDM' is the same as the predecessor.
79bca07a45SDimitry Andric // (7) The LocationContext is the same as the predecessor.
80809500fcSDimitry Andric // (8) Expressions that are *not* lvalue expressions.
81809500fcSDimitry Andric // (9) The PostStmt isn't for a non-consumed Stmt or Expr.
829f4dbff6SDimitry Andric // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
839f4dbff6SDimitry Andric // PreImplicitCall (so that we would be able to find it when retrying a
849f4dbff6SDimitry Andric // call with no inlining).
8556d91b49SDimitry Andric // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
86bca07a45SDimitry Andric
87bca07a45SDimitry Andric // Conditions 1 and 2.
88bca07a45SDimitry Andric if (node->pred_size() != 1 || node->succ_size() != 1)
89dbe13110SDimitry Andric return false;
90bca07a45SDimitry Andric
91dbe13110SDimitry Andric const ExplodedNode *pred = *(node->pred_begin());
92bca07a45SDimitry Andric if (pred->succ_size() != 1)
93dbe13110SDimitry Andric return false;
94bca07a45SDimitry Andric
95dbe13110SDimitry Andric const ExplodedNode *succ = *(node->succ_begin());
96bca07a45SDimitry Andric if (succ->pred_size() != 1)
97dbe13110SDimitry Andric return false;
98bca07a45SDimitry Andric
99809500fcSDimitry Andric // Now reclaim any nodes that are (by definition) not essential to
100809500fcSDimitry Andric // analysis history and are not consulted by any client code.
101bca07a45SDimitry Andric ProgramPoint progPoint = node->getLocation();
102809500fcSDimitry Andric if (progPoint.getAs<PreStmtPurgeDeadSymbols>())
103809500fcSDimitry Andric return !progPoint.getTag();
104809500fcSDimitry Andric
105809500fcSDimitry Andric // Condition 3.
106809500fcSDimitry Andric if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>())
107dbe13110SDimitry Andric return false;
108dbe13110SDimitry Andric
109bca07a45SDimitry Andric // Condition 4.
110809500fcSDimitry Andric if (progPoint.getTag())
111dbe13110SDimitry Andric return false;
112bca07a45SDimitry Andric
113bca07a45SDimitry Andric // Conditions 5, 6, and 7.
114dbe13110SDimitry Andric ProgramStateRef state = node->getState();
115dbe13110SDimitry Andric ProgramStateRef pred_state = pred->getState();
116bca07a45SDimitry Andric if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
117bca07a45SDimitry Andric progPoint.getLocationContext() != pred->getLocationContext())
118dbe13110SDimitry Andric return false;
119bca07a45SDimitry Andric
120809500fcSDimitry Andric // All further checks require expressions. As per #3, we know that we have
121809500fcSDimitry Andric // a PostStmt.
122809500fcSDimitry Andric const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt());
123809500fcSDimitry Andric if (!Ex)
124809500fcSDimitry Andric return false;
125809500fcSDimitry Andric
126bca07a45SDimitry Andric // Condition 8.
127809500fcSDimitry Andric // Do not collect nodes for "interesting" lvalue expressions since they are
128809500fcSDimitry Andric // used extensively for generating path diagnostics.
129809500fcSDimitry Andric if (isInterestingLValueExpr(Ex))
130809500fcSDimitry Andric return false;
131809500fcSDimitry Andric
132809500fcSDimitry Andric // Condition 9.
13313cc256eSDimitry Andric // Do not collect nodes for non-consumed Stmt or Expr to ensure precise
13413cc256eSDimitry Andric // diagnostic generation; specifically, so that we could anchor arrows
13513cc256eSDimitry Andric // pointing to the beginning of statements (as written in code).
136519fc96cSDimitry Andric const ParentMap &PM = progPoint.getLocationContext()->getParentMap();
137dbe13110SDimitry Andric if (!PM.isConsumedExpr(Ex))
138dbe13110SDimitry Andric return false;
139bca07a45SDimitry Andric
140809500fcSDimitry Andric // Condition 10.
14156d91b49SDimitry Andric const ProgramPoint SuccLoc = succ->getLocation();
142e3b55780SDimitry Andric if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
14313cc256eSDimitry Andric if (CallEvent::isCallStmt(SP->getStmt()))
14456d91b49SDimitry Andric return false;
14556d91b49SDimitry Andric
1469f4dbff6SDimitry Andric // Condition 10, continuation.
1479f4dbff6SDimitry Andric if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>())
1489f4dbff6SDimitry Andric return false;
1499f4dbff6SDimitry Andric
150dbe13110SDimitry Andric return true;
151dbe13110SDimitry Andric }
152dbe13110SDimitry Andric
collectNode(ExplodedNode * node)153dbe13110SDimitry Andric void ExplodedGraph::collectNode(ExplodedNode *node) {
154dbe13110SDimitry Andric // Removing a node means:
155bca07a45SDimitry Andric // (a) changing the predecessors successor to the successor of this node
156bca07a45SDimitry Andric // (b) changing the successors predecessor to the predecessor of this node
157bca07a45SDimitry Andric // (c) Putting 'node' onto freeNodes.
158dbe13110SDimitry Andric assert(node->pred_size() == 1 || node->succ_size() == 1);
159dbe13110SDimitry Andric ExplodedNode *pred = *(node->pred_begin());
160dbe13110SDimitry Andric ExplodedNode *succ = *(node->succ_begin());
161bca07a45SDimitry Andric pred->replaceSuccessor(succ);
162bca07a45SDimitry Andric succ->replacePredecessor(pred);
163dbe13110SDimitry Andric FreeNodes.push_back(node);
164bca07a45SDimitry Andric Nodes.RemoveNode(node);
165bca07a45SDimitry Andric --NumNodes;
166bca07a45SDimitry Andric node->~ExplodedNode();
167bca07a45SDimitry Andric }
168bca07a45SDimitry Andric
reclaimRecentlyAllocatedNodes()169dbe13110SDimitry Andric void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
170dbe13110SDimitry Andric if (ChangedNodes.empty())
171dbe13110SDimitry Andric return;
172dbe13110SDimitry Andric
17313cc256eSDimitry Andric // Only periodically reclaim nodes so that we can build up a set of
174dbe13110SDimitry Andric // nodes that meet the reclamation criteria. Freshly created nodes
175dbe13110SDimitry Andric // by definition have no successor, and thus cannot be reclaimed (see below).
17613cc256eSDimitry Andric assert(ReclaimCounter > 0);
17713cc256eSDimitry Andric if (--ReclaimCounter != 0)
178dbe13110SDimitry Andric return;
17913cc256eSDimitry Andric ReclaimCounter = ReclaimNodeInterval;
180dbe13110SDimitry Andric
18148675466SDimitry Andric for (const auto node : ChangedNodes)
182dbe13110SDimitry Andric if (shouldCollect(node))
183dbe13110SDimitry Andric collectNode(node);
184dbe13110SDimitry Andric ChangedNodes.clear();
185bca07a45SDimitry Andric }
186bca07a45SDimitry Andric
187bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
1884c8b2481SRoman Divacky // ExplodedNode.
189ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
190ec2b103cSEd Schouten
19113cc256eSDimitry Andric // An NodeGroup's storage type is actually very much like a TinyPtrVector:
19213cc256eSDimitry Andric // it can be either a pointer to a single ExplodedNode, or a pointer to a
19313cc256eSDimitry Andric // BumpVector allocated with the ExplodedGraph's allocator. This allows the
19413cc256eSDimitry Andric // common case of single-node NodeGroups to be implemented with no extra memory.
19513cc256eSDimitry Andric //
19613cc256eSDimitry Andric // Consequently, each of the NodeGroup methods have up to four cases to handle:
19713cc256eSDimitry Andric // 1. The flag is set and this group does not actually contain any nodes.
19813cc256eSDimitry Andric // 2. The group is empty, in which case the storage value is null.
19913cc256eSDimitry Andric // 3. The group contains a single node.
20013cc256eSDimitry Andric // 4. The group contains more than one node.
20148675466SDimitry Andric using ExplodedNodeVector = BumpVector<ExplodedNode *>;
20248675466SDimitry Andric using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
203ec2b103cSEd Schouten
addPredecessor(ExplodedNode * V,ExplodedGraph & G)2044c8b2481SRoman Divacky void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
205ec2b103cSEd Schouten assert(!V->isSink());
2064c8b2481SRoman Divacky Preds.addNode(V, G);
2074c8b2481SRoman Divacky V->Succs.addNode(this, G);
208ec2b103cSEd Schouten }
209ec2b103cSEd Schouten
replaceNode(ExplodedNode * node)210bca07a45SDimitry Andric void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
21113cc256eSDimitry Andric assert(!getFlag());
21213cc256eSDimitry Andric
21313cc256eSDimitry Andric GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
21413cc256eSDimitry Andric assert(Storage.is<ExplodedNode *>());
21513cc256eSDimitry Andric Storage = node;
21613cc256eSDimitry Andric assert(Storage.is<ExplodedNode *>());
217bca07a45SDimitry Andric }
218bca07a45SDimitry Andric
addNode(ExplodedNode * N,ExplodedGraph & G)2194c8b2481SRoman Divacky void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
220ec2b103cSEd Schouten assert(!getFlag());
221ec2b103cSEd Schouten
22213cc256eSDimitry Andric GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
22313cc256eSDimitry Andric if (Storage.isNull()) {
22413cc256eSDimitry Andric Storage = N;
22513cc256eSDimitry Andric assert(Storage.is<ExplodedNode *>());
22613cc256eSDimitry Andric return;
22713cc256eSDimitry Andric }
2284c8b2481SRoman Divacky
22913cc256eSDimitry Andric ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();
23013cc256eSDimitry Andric
23113cc256eSDimitry Andric if (!V) {
23213cc256eSDimitry Andric // Switch from single-node to multi-node representation.
23313cc256eSDimitry Andric ExplodedNode *Old = Storage.get<ExplodedNode *>();
23413cc256eSDimitry Andric
23513cc256eSDimitry Andric BumpVectorContext &Ctx = G.getNodeAllocator();
2367fa27ce4SDimitry Andric V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4);
23713cc256eSDimitry Andric V->push_back(Old, Ctx);
23813cc256eSDimitry Andric
23913cc256eSDimitry Andric Storage = V;
24013cc256eSDimitry Andric assert(!getFlag());
24113cc256eSDimitry Andric assert(Storage.is<ExplodedNodeVector *>());
242ec2b103cSEd Schouten }
24313cc256eSDimitry Andric
24413cc256eSDimitry Andric V->push_back(N, G.getNodeAllocator());
245ec2b103cSEd Schouten }
246ec2b103cSEd Schouten
size() const2474c8b2481SRoman Divacky unsigned ExplodedNode::NodeGroup::size() const {
248ec2b103cSEd Schouten if (getFlag())
249ec2b103cSEd Schouten return 0;
250ec2b103cSEd Schouten
25113cc256eSDimitry Andric const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
25213cc256eSDimitry Andric if (Storage.isNull())
25313cc256eSDimitry Andric return 0;
25413cc256eSDimitry Andric if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
25513cc256eSDimitry Andric return V->size();
25613cc256eSDimitry Andric return 1;
257ec2b103cSEd Schouten }
258ec2b103cSEd Schouten
begin() const25913cc256eSDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::begin() const {
260ec2b103cSEd Schouten if (getFlag())
2619f4dbff6SDimitry Andric return nullptr;
262ec2b103cSEd Schouten
26313cc256eSDimitry Andric const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
26413cc256eSDimitry Andric if (Storage.isNull())
2659f4dbff6SDimitry Andric return nullptr;
26613cc256eSDimitry Andric if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
26713cc256eSDimitry Andric return V->begin();
26813cc256eSDimitry Andric return Storage.getAddrOfPtr1();
269ec2b103cSEd Schouten }
270ec2b103cSEd Schouten
end() const27113cc256eSDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
272ec2b103cSEd Schouten if (getFlag())
2739f4dbff6SDimitry Andric return nullptr;
274ec2b103cSEd Schouten
27513cc256eSDimitry Andric const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
27613cc256eSDimitry Andric if (Storage.isNull())
2779f4dbff6SDimitry Andric return nullptr;
27813cc256eSDimitry Andric if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
27913cc256eSDimitry Andric return V->end();
28013cc256eSDimitry Andric return Storage.getAddrOfPtr1() + 1;
281ec2b103cSEd Schouten }
282ec2b103cSEd Schouten
isTrivial() const283676fbe81SDimitry Andric bool ExplodedNode::isTrivial() const {
284676fbe81SDimitry Andric return pred_size() == 1 && succ_size() == 1 &&
285676fbe81SDimitry Andric getFirstPred()->getState()->getID() == getState()->getID() &&
286676fbe81SDimitry Andric getFirstPred()->succ_size() == 1;
287676fbe81SDimitry Andric }
288676fbe81SDimitry Andric
getCFGBlock() const289519fc96cSDimitry Andric const CFGBlock *ExplodedNode::getCFGBlock() const {
290519fc96cSDimitry Andric ProgramPoint P = getLocation();
291519fc96cSDimitry Andric if (auto BEP = P.getAs<BlockEntrance>())
292519fc96cSDimitry Andric return BEP->getBlock();
293519fc96cSDimitry Andric
294519fc96cSDimitry Andric // Find the node's current statement in the CFG.
295519fc96cSDimitry Andric // FIXME: getStmtForDiagnostics() does nasty things in order to provide
296519fc96cSDimitry Andric // a valid statement for body farms, do we need this behavior here?
297519fc96cSDimitry Andric if (const Stmt *S = getStmtForDiagnostics())
298519fc96cSDimitry Andric return getLocationContext()
299519fc96cSDimitry Andric ->getAnalysisDeclContext()
300519fc96cSDimitry Andric ->getCFGStmtMap()
301519fc96cSDimitry Andric ->getBlock(S);
302519fc96cSDimitry Andric
303519fc96cSDimitry Andric return nullptr;
304519fc96cSDimitry Andric }
305519fc96cSDimitry Andric
306519fc96cSDimitry Andric static const LocationContext *
findTopAutosynthesizedParentContext(const LocationContext * LC)307519fc96cSDimitry Andric findTopAutosynthesizedParentContext(const LocationContext *LC) {
308519fc96cSDimitry Andric assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
309519fc96cSDimitry Andric const LocationContext *ParentLC = LC->getParent();
310519fc96cSDimitry Andric assert(ParentLC && "We don't start analysis from autosynthesized code");
311519fc96cSDimitry Andric while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
312519fc96cSDimitry Andric LC = ParentLC;
313519fc96cSDimitry Andric ParentLC = LC->getParent();
314519fc96cSDimitry Andric assert(ParentLC && "We don't start analysis from autosynthesized code");
315519fc96cSDimitry Andric }
316519fc96cSDimitry Andric return LC;
317519fc96cSDimitry Andric }
318519fc96cSDimitry Andric
getStmtForDiagnostics() const319519fc96cSDimitry Andric const Stmt *ExplodedNode::getStmtForDiagnostics() const {
320519fc96cSDimitry Andric // We cannot place diagnostics on autosynthesized code.
321519fc96cSDimitry Andric // Put them onto the call site through which we jumped into autosynthesized
322519fc96cSDimitry Andric // code for the first time.
323519fc96cSDimitry Andric const LocationContext *LC = getLocationContext();
324519fc96cSDimitry Andric if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
325519fc96cSDimitry Andric // It must be a stack frame because we only autosynthesize functions.
326519fc96cSDimitry Andric return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
327519fc96cSDimitry Andric ->getCallSite();
328519fc96cSDimitry Andric }
329519fc96cSDimitry Andric // Otherwise, see if the node's program point directly points to a statement.
330519fc96cSDimitry Andric // FIXME: Refactor into a ProgramPoint method?
331519fc96cSDimitry Andric ProgramPoint P = getLocation();
332519fc96cSDimitry Andric if (auto SP = P.getAs<StmtPoint>())
333519fc96cSDimitry Andric return SP->getStmt();
334519fc96cSDimitry Andric if (auto BE = P.getAs<BlockEdge>())
335519fc96cSDimitry Andric return BE->getSrc()->getTerminatorStmt();
336519fc96cSDimitry Andric if (auto CE = P.getAs<CallEnter>())
337519fc96cSDimitry Andric return CE->getCallExpr();
338519fc96cSDimitry Andric if (auto CEE = P.getAs<CallExitEnd>())
339519fc96cSDimitry Andric return CEE->getCalleeContext()->getCallSite();
340519fc96cSDimitry Andric if (auto PIPP = P.getAs<PostInitializer>())
341519fc96cSDimitry Andric return PIPP->getInitializer()->getInit();
342519fc96cSDimitry Andric if (auto CEB = P.getAs<CallExitBegin>())
343519fc96cSDimitry Andric return CEB->getReturnStmt();
344519fc96cSDimitry Andric if (auto FEP = P.getAs<FunctionExitPoint>())
345519fc96cSDimitry Andric return FEP->getStmt();
346519fc96cSDimitry Andric
347519fc96cSDimitry Andric return nullptr;
348519fc96cSDimitry Andric }
349519fc96cSDimitry Andric
getNextStmtForDiagnostics() const350519fc96cSDimitry Andric const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
351519fc96cSDimitry Andric for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
352519fc96cSDimitry Andric if (const Stmt *S = N->getStmtForDiagnostics()) {
353519fc96cSDimitry Andric // Check if the statement is '?' or '&&'/'||'. These are "merges",
354519fc96cSDimitry Andric // not actual statement points.
355519fc96cSDimitry Andric switch (S->getStmtClass()) {
356519fc96cSDimitry Andric case Stmt::ChooseExprClass:
357519fc96cSDimitry Andric case Stmt::BinaryConditionalOperatorClass:
358519fc96cSDimitry Andric case Stmt::ConditionalOperatorClass:
359519fc96cSDimitry Andric continue;
360519fc96cSDimitry Andric case Stmt::BinaryOperatorClass: {
361519fc96cSDimitry Andric BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
362519fc96cSDimitry Andric if (Op == BO_LAnd || Op == BO_LOr)
363519fc96cSDimitry Andric continue;
364519fc96cSDimitry Andric break;
365519fc96cSDimitry Andric }
366519fc96cSDimitry Andric default:
367519fc96cSDimitry Andric break;
368519fc96cSDimitry Andric }
369519fc96cSDimitry Andric // We found the statement, so return it.
370519fc96cSDimitry Andric return S;
371519fc96cSDimitry Andric }
372519fc96cSDimitry Andric }
373519fc96cSDimitry Andric
374519fc96cSDimitry Andric return nullptr;
375519fc96cSDimitry Andric }
376519fc96cSDimitry Andric
getPreviousStmtForDiagnostics() const377519fc96cSDimitry Andric const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
378519fc96cSDimitry Andric for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
379519fc96cSDimitry Andric if (const Stmt *S = N->getStmtForDiagnostics())
380519fc96cSDimitry Andric return S;
381519fc96cSDimitry Andric
382519fc96cSDimitry Andric return nullptr;
383519fc96cSDimitry Andric }
384519fc96cSDimitry Andric
getCurrentOrPreviousStmtForDiagnostics() const385519fc96cSDimitry Andric const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
386519fc96cSDimitry Andric if (const Stmt *S = getStmtForDiagnostics())
387519fc96cSDimitry Andric return S;
388519fc96cSDimitry Andric
389519fc96cSDimitry Andric return getPreviousStmtForDiagnostics();
390519fc96cSDimitry Andric }
391519fc96cSDimitry Andric
getNode(const ProgramPoint & L,ProgramStateRef State,bool IsSink,bool * IsNew)3924c8b2481SRoman Divacky ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
393dbe13110SDimitry Andric ProgramStateRef State,
394dbe13110SDimitry Andric bool IsSink,
395dbe13110SDimitry Andric bool* IsNew) {
3964c8b2481SRoman Divacky // Profile 'State' to determine if we already have an existing node.
3974c8b2481SRoman Divacky llvm::FoldingSetNodeID profile;
3989f4dbff6SDimitry Andric void *InsertPos = nullptr;
3994c8b2481SRoman Divacky
400dbe13110SDimitry Andric NodeTy::Profile(profile, L, State, IsSink);
4014c8b2481SRoman Divacky NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
4024c8b2481SRoman Divacky
4034c8b2481SRoman Divacky if (!V) {
404dbe13110SDimitry Andric if (!FreeNodes.empty()) {
405dbe13110SDimitry Andric V = FreeNodes.back();
406dbe13110SDimitry Andric FreeNodes.pop_back();
407bca07a45SDimitry Andric }
408bca07a45SDimitry Andric else {
4094c8b2481SRoman Divacky // Allocate a new node.
4107fa27ce4SDimitry Andric V = getAllocator().Allocate<NodeTy>();
411bca07a45SDimitry Andric }
412bca07a45SDimitry Andric
413519fc96cSDimitry Andric ++NumNodes;
414519fc96cSDimitry Andric new (V) NodeTy(L, State, NumNodes, IsSink);
4154c8b2481SRoman Divacky
41613cc256eSDimitry Andric if (ReclaimNodeInterval)
417dbe13110SDimitry Andric ChangedNodes.push_back(V);
418bca07a45SDimitry Andric
4194c8b2481SRoman Divacky // Insert the node into the node set and return it.
4204c8b2481SRoman Divacky Nodes.InsertNode(V, InsertPos);
4214c8b2481SRoman Divacky
4224c8b2481SRoman Divacky if (IsNew) *IsNew = true;
4234c8b2481SRoman Divacky }
4244c8b2481SRoman Divacky else
4254c8b2481SRoman Divacky if (IsNew) *IsNew = false;
4264c8b2481SRoman Divacky
4274c8b2481SRoman Divacky return V;
428ec2b103cSEd Schouten }
429ec2b103cSEd Schouten
createUncachedNode(const ProgramPoint & L,ProgramStateRef State,int64_t Id,bool IsSink)4302b6b257fSDimitry Andric ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
4312b6b257fSDimitry Andric ProgramStateRef State,
432519fc96cSDimitry Andric int64_t Id,
4332b6b257fSDimitry Andric bool IsSink) {
4347fa27ce4SDimitry Andric NodeTy *V = getAllocator().Allocate<NodeTy>();
435519fc96cSDimitry Andric new (V) NodeTy(L, State, Id, IsSink);
4362b6b257fSDimitry Andric return V;
4372b6b257fSDimitry Andric }
4382b6b257fSDimitry Andric
43906d4ba38SDimitry Andric std::unique_ptr<ExplodedGraph>
trim(ArrayRef<const NodeTy * > Sinks,InterExplodedGraphMap * ForwardMap,InterExplodedGraphMap * InverseMap) const440809500fcSDimitry Andric ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
441809500fcSDimitry Andric InterExplodedGraphMap *ForwardMap,
442809500fcSDimitry Andric InterExplodedGraphMap *InverseMap) const {
443809500fcSDimitry Andric if (Nodes.empty())
4449f4dbff6SDimitry Andric return nullptr;
4454c8b2481SRoman Divacky
44648675466SDimitry Andric using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
447ec2b103cSEd Schouten Pass1Ty Pass1;
448ec2b103cSEd Schouten
44948675466SDimitry Andric using Pass2Ty = InterExplodedGraphMap;
450809500fcSDimitry Andric InterExplodedGraphMap Pass2Scratch;
451809500fcSDimitry Andric Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
452ec2b103cSEd Schouten
45336981b17SDimitry Andric SmallVector<const ExplodedNode*, 10> WL1, WL2;
454ec2b103cSEd Schouten
455ec2b103cSEd Schouten // ===- Pass 1 (reverse DFS) -===
45648675466SDimitry Andric for (const auto Sink : Sinks)
45748675466SDimitry Andric if (Sink)
45848675466SDimitry Andric WL1.push_back(Sink);
459ec2b103cSEd Schouten
460809500fcSDimitry Andric // Process the first worklist until it is empty.
461ec2b103cSEd Schouten while (!WL1.empty()) {
462bfef3995SDimitry Andric const ExplodedNode *N = WL1.pop_back_val();
463ec2b103cSEd Schouten
464ec2b103cSEd Schouten // Have we already visited this node? If so, continue to the next one.
46506d4ba38SDimitry Andric if (!Pass1.insert(N).second)
466ec2b103cSEd Schouten continue;
467ec2b103cSEd Schouten
468ec2b103cSEd Schouten // If this is a root enqueue it to the second worklist.
469ec2b103cSEd Schouten if (N->Preds.empty()) {
470ec2b103cSEd Schouten WL2.push_back(N);
471ec2b103cSEd Schouten continue;
472ec2b103cSEd Schouten }
473ec2b103cSEd Schouten
474ec2b103cSEd Schouten // Visit our predecessors and enqueue them.
47506d4ba38SDimitry Andric WL1.append(N->Preds.begin(), N->Preds.end());
476ec2b103cSEd Schouten }
477ec2b103cSEd Schouten
478ec2b103cSEd Schouten // We didn't hit a root? Return with a null pointer for the new graph.
479ec2b103cSEd Schouten if (WL2.empty())
4809f4dbff6SDimitry Andric return nullptr;
481ec2b103cSEd Schouten
482ec2b103cSEd Schouten // Create an empty graph.
48306d4ba38SDimitry Andric std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
484ec2b103cSEd Schouten
485ec2b103cSEd Schouten // ===- Pass 2 (forward DFS to construct the new graph) -===
486ec2b103cSEd Schouten while (!WL2.empty()) {
487bfef3995SDimitry Andric const ExplodedNode *N = WL2.pop_back_val();
488ec2b103cSEd Schouten
489ec2b103cSEd Schouten // Skip this node if we have already processed it.
4907fa27ce4SDimitry Andric if (Pass2.contains(N))
491ec2b103cSEd Schouten continue;
492ec2b103cSEd Schouten
493ec2b103cSEd Schouten // Create the corresponding node in the new graph and record the mapping
494ec2b103cSEd Schouten // from the old node to the new node.
495519fc96cSDimitry Andric ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
496519fc96cSDimitry Andric N->getID(), N->isSink());
497ec2b103cSEd Schouten Pass2[N] = NewN;
498ec2b103cSEd Schouten
499ec2b103cSEd Schouten // Also record the reverse mapping from the new node to the old node.
500ec2b103cSEd Schouten if (InverseMap) (*InverseMap)[NewN] = N;
501ec2b103cSEd Schouten
502ec2b103cSEd Schouten // If this node is a root, designate it as such in the graph.
503ec2b103cSEd Schouten if (N->Preds.empty())
504ec2b103cSEd Schouten G->addRoot(NewN);
505ec2b103cSEd Schouten
506ec2b103cSEd Schouten // In the case that some of the intended predecessors of NewN have already
507ec2b103cSEd Schouten // been created, we should hook them up as predecessors.
508ec2b103cSEd Schouten
509ec2b103cSEd Schouten // Walk through the predecessors of 'N' and hook up their corresponding
510ec2b103cSEd Schouten // nodes in the new graph (if any) to the freshly created node.
5117fa27ce4SDimitry Andric for (const ExplodedNode *Pred : N->Preds) {
5127fa27ce4SDimitry Andric Pass2Ty::iterator PI = Pass2.find(Pred);
513ec2b103cSEd Schouten if (PI == Pass2.end())
514ec2b103cSEd Schouten continue;
515ec2b103cSEd Schouten
516809500fcSDimitry Andric NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G);
517ec2b103cSEd Schouten }
518ec2b103cSEd Schouten
519ec2b103cSEd Schouten // In the case that some of the intended successors of NewN have already
520ec2b103cSEd Schouten // been created, we should hook them up as successors. Otherwise, enqueue
521ec2b103cSEd Schouten // the new nodes from the original graph that should have nodes created
522ec2b103cSEd Schouten // in the new graph.
5237fa27ce4SDimitry Andric for (const ExplodedNode *Succ : N->Succs) {
5247fa27ce4SDimitry Andric Pass2Ty::iterator PI = Pass2.find(Succ);
525ec2b103cSEd Schouten if (PI != Pass2.end()) {
526809500fcSDimitry Andric const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
527ec2b103cSEd Schouten continue;
528ec2b103cSEd Schouten }
529ec2b103cSEd Schouten
530ec2b103cSEd Schouten // Enqueue nodes to the worklist that were marked during pass 1.
5317fa27ce4SDimitry Andric if (Pass1.count(Succ))
5327fa27ce4SDimitry Andric WL2.push_back(Succ);
533ec2b103cSEd Schouten }
534ec2b103cSEd Schouten }
535ec2b103cSEd Schouten
536ec2b103cSEd Schouten return G;
537ec2b103cSEd Schouten }
538