/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines the template classes ExplodedNode and ExplodedGraph, |
10 | | // which represent a path-sensitive, intra-procedural "exploded graph." |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
15 | | #include "clang/AST/Expr.h" |
16 | | #include "clang/AST/ExprObjC.h" |
17 | | #include "clang/AST/ParentMap.h" |
18 | | #include "clang/AST/Stmt.h" |
19 | | #include "clang/Analysis/CFGStmtMap.h" |
20 | | #include "clang/Analysis/ProgramPoint.h" |
21 | | #include "clang/Analysis/Support/BumpVector.h" |
22 | | #include "clang/Basic/LLVM.h" |
23 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
24 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
25 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
26 | | #include "llvm/ADT/DenseSet.h" |
27 | | #include "llvm/ADT/FoldingSet.h" |
28 | | #include "llvm/ADT/PointerUnion.h" |
29 | | #include "llvm/ADT/SmallVector.h" |
30 | | #include "llvm/Support/Casting.h" |
31 | | #include <cassert> |
32 | | #include <memory> |
33 | | #include <optional> |
34 | | |
35 | | using namespace clang; |
36 | | using namespace ento; |
37 | | |
38 | | //===----------------------------------------------------------------------===// |
39 | | // Cleanup. |
40 | | //===----------------------------------------------------------------------===// |
41 | | |
42 | 58.0k | ExplodedGraph::ExplodedGraph() = default; |
43 | | |
44 | 58.0k | ExplodedGraph::~ExplodedGraph() = default; |
45 | | |
46 | | //===----------------------------------------------------------------------===// |
47 | | // Node reclamation. |
48 | | //===----------------------------------------------------------------------===// |
49 | | |
50 | 1.07M | bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) { |
51 | 1.07M | if (!Ex->isLValue()) |
52 | 606k | return false; |
53 | 472k | return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex); |
54 | 1.07M | } |
55 | | |
56 | 1.77M | bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { |
57 | | // First, we only consider nodes for reclamation of the following |
58 | | // conditions apply: |
59 | | // |
60 | | // (1) 1 predecessor (that has one successor) |
61 | | // (2) 1 successor (that has one predecessor) |
62 | | // |
63 | | // If a node has no successor it is on the "frontier", while a node |
64 | | // with no predecessor is a root. |
65 | | // |
66 | | // After these prerequisites, we discard all "filler" nodes that |
67 | | // are used only for intermediate processing, and are not essential |
68 | | // for analyzer history: |
69 | | // |
70 | | // (a) PreStmtPurgeDeadSymbols |
71 | | // |
72 | | // We then discard all other nodes where *all* of the following conditions |
73 | | // apply: |
74 | | // |
75 | | // (3) The ProgramPoint is for a PostStmt, but not a PostStore. |
76 | | // (4) There is no 'tag' for the ProgramPoint. |
77 | | // (5) The 'store' is the same as the predecessor. |
78 | | // (6) The 'GDM' is the same as the predecessor. |
79 | | // (7) The LocationContext is the same as the predecessor. |
80 | | // (8) Expressions that are *not* lvalue expressions. |
81 | | // (9) The PostStmt isn't for a non-consumed Stmt or Expr. |
82 | | // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or |
83 | | // PreImplicitCall (so that we would be able to find it when retrying a |
84 | | // call with no inlining). |
85 | | // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. |
86 | | |
87 | | // Conditions 1 and 2. |
88 | 1.77M | if (node->pred_size() != 1 || node->succ_size() != 11.77M ) |
89 | 85.1k | return false; |
90 | | |
91 | 1.69M | const ExplodedNode *pred = *(node->pred_begin()); |
92 | 1.69M | if (pred->succ_size() != 1) |
93 | 60.9k | return false; |
94 | | |
95 | 1.63M | const ExplodedNode *succ = *(node->succ_begin()); |
96 | 1.63M | if (succ->pred_size() != 1) |
97 | 4.67k | return false; |
98 | | |
99 | | // Now reclaim any nodes that are (by definition) not essential to |
100 | | // analysis history and are not consulted by any client code. |
101 | 1.62M | ProgramPoint progPoint = node->getLocation(); |
102 | 1.62M | if (progPoint.getAs<PreStmtPurgeDeadSymbols>()) |
103 | 163k | return !progPoint.getTag(); |
104 | | |
105 | | // Condition 3. |
106 | 1.46M | if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>()1.21M ) |
107 | 299k | return false; |
108 | | |
109 | | // Condition 4. |
110 | 1.16M | if (progPoint.getTag()) |
111 | 62.2k | return false; |
112 | | |
113 | | // Conditions 5, 6, and 7. |
114 | 1.10M | ProgramStateRef state = node->getState(); |
115 | 1.10M | ProgramStateRef pred_state = pred->getState(); |
116 | 1.10M | if (state->store != pred_state->store || state->GDM != pred_state->GDM1.10M || |
117 | 1.10M | progPoint.getLocationContext() != pred->getLocationContext()1.10M ) |
118 | 2.94k | return false; |
119 | | |
120 | | // All further checks require expressions. As per #3, we know that we have |
121 | | // a PostStmt. |
122 | 1.10M | const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt()); |
123 | 1.10M | if (!Ex) |
124 | 28.1k | return false; |
125 | | |
126 | | // Condition 8. |
127 | | // Do not collect nodes for "interesting" lvalue expressions since they are |
128 | | // used extensively for generating path diagnostics. |
129 | 1.07M | if (isInterestingLValueExpr(Ex)) |
130 | 438k | return false; |
131 | | |
132 | | // Condition 9. |
133 | | // Do not collect nodes for non-consumed Stmt or Expr to ensure precise |
134 | | // diagnostic generation; specifically, so that we could anchor arrows |
135 | | // pointing to the beginning of statements (as written in code). |
136 | 633k | const ParentMap &PM = progPoint.getLocationContext()->getParentMap(); |
137 | 633k | if (!PM.isConsumedExpr(Ex)) |
138 | 25.3k | return false; |
139 | | |
140 | | // Condition 10. |
141 | 608k | const ProgramPoint SuccLoc = succ->getLocation(); |
142 | 608k | if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) |
143 | 593k | if (CallEvent::isCallStmt(SP->getStmt())) |
144 | 32.1k | return false; |
145 | | |
146 | | // Condition 10, continuation. |
147 | 576k | if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>()576k ) |
148 | 83 | return false; |
149 | | |
150 | 576k | return true; |
151 | 576k | } |
152 | | |
153 | 576k | void ExplodedGraph::collectNode(ExplodedNode *node) { |
154 | | // Removing a node means: |
155 | | // (a) changing the predecessors successor to the successor of this node |
156 | | // (b) changing the successors predecessor to the predecessor of this node |
157 | | // (c) Putting 'node' onto freeNodes. |
158 | 576k | assert(node->pred_size() == 1 || node->succ_size() == 1); |
159 | 576k | ExplodedNode *pred = *(node->pred_begin()); |
160 | 576k | ExplodedNode *succ = *(node->succ_begin()); |
161 | 576k | pred->replaceSuccessor(succ); |
162 | 576k | succ->replacePredecessor(pred); |
163 | 576k | FreeNodes.push_back(node); |
164 | 576k | Nodes.RemoveNode(node); |
165 | 576k | --NumNodes; |
166 | 576k | node->~ExplodedNode(); |
167 | 576k | } |
168 | | |
169 | 1.33M | void ExplodedGraph::reclaimRecentlyAllocatedNodes() { |
170 | 1.33M | if (ChangedNodes.empty()) |
171 | 5 | return; |
172 | | |
173 | | // Only periodically reclaim nodes so that we can build up a set of |
174 | | // nodes that meet the reclamation criteria. Freshly created nodes |
175 | | // by definition have no successor, and thus cannot be reclaimed (see below). |
176 | 1.33M | assert(ReclaimCounter > 0); |
177 | 1.33M | if (--ReclaimCounter != 0) |
178 | 1.33M | return; |
179 | 800 | ReclaimCounter = ReclaimNodeInterval; |
180 | | |
181 | 800 | for (const auto node : ChangedNodes) |
182 | 1.77M | if (shouldCollect(node)) |
183 | 576k | collectNode(node); |
184 | 800 | ChangedNodes.clear(); |
185 | 800 | } |
186 | | |
187 | | //===----------------------------------------------------------------------===// |
188 | | // ExplodedNode. |
189 | | //===----------------------------------------------------------------------===// |
190 | | |
191 | | // An NodeGroup's storage type is actually very much like a TinyPtrVector: |
192 | | // it can be either a pointer to a single ExplodedNode, or a pointer to a |
193 | | // BumpVector allocated with the ExplodedGraph's allocator. This allows the |
194 | | // common case of single-node NodeGroups to be implemented with no extra memory. |
195 | | // |
196 | | // Consequently, each of the NodeGroup methods have up to four cases to handle: |
197 | | // 1. The flag is set and this group does not actually contain any nodes. |
198 | | // 2. The group is empty, in which case the storage value is null. |
199 | | // 3. The group contains a single node. |
200 | | // 4. The group contains more than one node. |
201 | | using ExplodedNodeVector = BumpVector<ExplodedNode *>; |
202 | | using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>; |
203 | | |
204 | 10.7M | void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) { |
205 | 10.7M | assert(!V->isSink()); |
206 | 10.7M | Preds.addNode(V, G); |
207 | 10.7M | V->Succs.addNode(this, G); |
208 | 10.7M | } |
209 | | |
210 | 1.15M | void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) { |
211 | 1.15M | assert(!getFlag()); |
212 | | |
213 | 1.15M | GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P); |
214 | 1.15M | assert(Storage.is<ExplodedNode *>()); |
215 | 1.15M | Storage = node; |
216 | 1.15M | assert(Storage.is<ExplodedNode *>()); |
217 | 1.15M | } |
218 | | |
219 | 21.4M | void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) { |
220 | 21.4M | assert(!getFlag()); |
221 | | |
222 | 21.4M | GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P); |
223 | 21.4M | if (Storage.isNull()) { |
224 | 21.3M | Storage = N; |
225 | 21.3M | assert(Storage.is<ExplodedNode *>()); |
226 | 21.3M | return; |
227 | 21.3M | } |
228 | | |
229 | 79.3k | ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>(); |
230 | | |
231 | 79.3k | if (!V) { |
232 | | // Switch from single-node to multi-node representation. |
233 | 61.4k | ExplodedNode *Old = Storage.get<ExplodedNode *>(); |
234 | | |
235 | 61.4k | BumpVectorContext &Ctx = G.getNodeAllocator(); |
236 | 61.4k | V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4); |
237 | 61.4k | V->push_back(Old, Ctx); |
238 | | |
239 | 61.4k | Storage = V; |
240 | 61.4k | assert(!getFlag()); |
241 | 61.4k | assert(Storage.is<ExplodedNodeVector *>()); |
242 | 61.4k | } |
243 | | |
244 | 79.3k | V->push_back(N, G.getNodeAllocator()); |
245 | 79.3k | } |
246 | | |
247 | 7.56M | unsigned ExplodedNode::NodeGroup::size() const { |
248 | 7.56M | if (getFlag()) |
249 | 3.66k | return 0; |
250 | | |
251 | 7.56M | const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); |
252 | 7.56M | if (Storage.isNull()) |
253 | 50.2k | return 0; |
254 | 7.51M | if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) |
255 | 98.4k | return V->size(); |
256 | 7.41M | return 1; |
257 | 7.51M | } |
258 | | |
259 | 41.6M | ExplodedNode * const *ExplodedNode::NodeGroup::begin() const { |
260 | 41.6M | if (getFlag()) |
261 | 4.37k | return nullptr; |
262 | | |
263 | 41.6M | const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); |
264 | 41.6M | if (Storage.isNull()) |
265 | 22.4k | return nullptr; |
266 | 41.6M | if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) |
267 | 46.6k | return V->begin(); |
268 | 41.6M | return Storage.getAddrOfPtr1(); |
269 | 41.6M | } |
270 | | |
271 | 19.7M | ExplodedNode * const *ExplodedNode::NodeGroup::end() const { |
272 | 19.7M | if (getFlag()) |
273 | 4.37k | return nullptr; |
274 | | |
275 | 19.7M | const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); |
276 | 19.7M | if (Storage.isNull()) |
277 | 22.4k | return nullptr; |
278 | 19.6M | if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) |
279 | 44.3k | return V->end(); |
280 | 19.6M | return Storage.getAddrOfPtr1() + 1; |
281 | 19.6M | } |
282 | | |
283 | 1.86k | bool ExplodedNode::isTrivial() const { |
284 | 1.86k | return pred_size() == 1 && succ_size() == 11.85k && |
285 | 1.86k | getFirstPred()->getState()->getID() == getState()->getID()1.76k && |
286 | 1.86k | getFirstPred()->succ_size() == 1944 ; |
287 | 1.86k | } |
288 | | |
289 | 168k | const CFGBlock *ExplodedNode::getCFGBlock() const { |
290 | 168k | ProgramPoint P = getLocation(); |
291 | 168k | if (auto BEP = P.getAs<BlockEntrance>()) |
292 | 11.1k | return BEP->getBlock(); |
293 | | |
294 | | // Find the node's current statement in the CFG. |
295 | | // FIXME: getStmtForDiagnostics() does nasty things in order to provide |
296 | | // a valid statement for body farms, do we need this behavior here? |
297 | 157k | if (const Stmt *S = getStmtForDiagnostics()) |
298 | 153k | return getLocationContext() |
299 | 153k | ->getAnalysisDeclContext() |
300 | 153k | ->getCFGStmtMap() |
301 | 153k | ->getBlock(S); |
302 | | |
303 | 3.48k | return nullptr; |
304 | 157k | } |
305 | | |
306 | | static const LocationContext * |
307 | 54.6k | findTopAutosynthesizedParentContext(const LocationContext *LC) { |
308 | 54.6k | assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); |
309 | 54.6k | const LocationContext *ParentLC = LC->getParent(); |
310 | 54.6k | assert(ParentLC && "We don't start analysis from autosynthesized code"); |
311 | 54.6k | while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { |
312 | 0 | LC = ParentLC; |
313 | 0 | ParentLC = LC->getParent(); |
314 | 0 | assert(ParentLC && "We don't start analysis from autosynthesized code"); |
315 | 0 | } |
316 | 54.6k | return LC; |
317 | 54.6k | } |
318 | | |
319 | 6.99M | const Stmt *ExplodedNode::getStmtForDiagnostics() const { |
320 | | // We cannot place diagnostics on autosynthesized code. |
321 | | // Put them onto the call site through which we jumped into autosynthesized |
322 | | // code for the first time. |
323 | 6.99M | const LocationContext *LC = getLocationContext(); |
324 | 6.99M | if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { |
325 | | // It must be a stack frame because we only autosynthesize functions. |
326 | 54.6k | return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) |
327 | 54.6k | ->getCallSite(); |
328 | 54.6k | } |
329 | | // Otherwise, see if the node's program point directly points to a statement. |
330 | | // FIXME: Refactor into a ProgramPoint method? |
331 | 6.94M | ProgramPoint P = getLocation(); |
332 | 6.94M | if (auto SP = P.getAs<StmtPoint>()) |
333 | 6.46M | return SP->getStmt(); |
334 | 480k | if (auto BE = P.getAs<BlockEdge>()) |
335 | 175k | return BE->getSrc()->getTerminatorStmt(); |
336 | 304k | if (auto CE = P.getAs<CallEnter>()) |
337 | 2.44k | return CE->getCallExpr(); |
338 | 302k | if (auto CEE = P.getAs<CallExitEnd>()) |
339 | 64.0k | return CEE->getCalleeContext()->getCallSite(); |
340 | 238k | if (auto PIPP = P.getAs<PostInitializer>()) |
341 | 24.9k | return PIPP->getInitializer()->getInit(); |
342 | 213k | if (auto CEB = P.getAs<CallExitBegin>()) |
343 | 58.9k | return CEB->getReturnStmt(); |
344 | 154k | if (auto FEP = P.getAs<FunctionExitPoint>()) |
345 | 15.2k | return FEP->getStmt(); |
346 | | |
347 | 139k | return nullptr; |
348 | 154k | } |
349 | | |
350 | 1.60k | const Stmt *ExplodedNode::getNextStmtForDiagnostics() const { |
351 | 2.98k | for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()1.38k ) { |
352 | 2.79k | if (const Stmt *S = N->getStmtForDiagnostics()) { |
353 | | // Check if the statement is '?' or '&&'/'||'. These are "merges", |
354 | | // not actual statement points. |
355 | 1.41k | switch (S->getStmtClass()) { |
356 | 0 | case Stmt::ChooseExprClass: |
357 | 2 | case Stmt::BinaryConditionalOperatorClass: |
358 | 2 | case Stmt::ConditionalOperatorClass: |
359 | 2 | continue; |
360 | 79 | case Stmt::BinaryOperatorClass: { |
361 | 79 | BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); |
362 | 79 | if (Op == BO_LAnd || Op == BO_LOr) |
363 | 0 | continue; |
364 | 79 | break; |
365 | 79 | } |
366 | 1.33k | default: |
367 | 1.33k | break; |
368 | 1.41k | } |
369 | | // We found the statement, so return it. |
370 | 1.41k | return S; |
371 | 1.41k | } |
372 | 2.79k | } |
373 | | |
374 | 192 | return nullptr; |
375 | 1.60k | } |
376 | | |
377 | 66 | const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const { |
378 | 150 | for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred()84 ) |
379 | 148 | if (const Stmt *S = N->getStmtForDiagnostics()) |
380 | 64 | return S; |
381 | | |
382 | 2 | return nullptr; |
383 | 66 | } |
384 | | |
385 | 30.0k | const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const { |
386 | 30.0k | if (const Stmt *S = getStmtForDiagnostics()) |
387 | 29.9k | return S; |
388 | | |
389 | 66 | return getPreviousStmtForDiagnostics(); |
390 | 30.0k | } |
391 | | |
392 | | ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, |
393 | | ProgramStateRef State, |
394 | | bool IsSink, |
395 | 3.21M | bool* IsNew) { |
396 | | // Profile 'State' to determine if we already have an existing node. |
397 | 3.21M | llvm::FoldingSetNodeID profile; |
398 | 3.21M | void *InsertPos = nullptr; |
399 | | |
400 | 3.21M | NodeTy::Profile(profile, L, State, IsSink); |
401 | 3.21M | NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); |
402 | | |
403 | 3.21M | if (!V) { |
404 | 3.20M | if (!FreeNodes.empty()) { |
405 | 562k | V = FreeNodes.back(); |
406 | 562k | FreeNodes.pop_back(); |
407 | 562k | } |
408 | 2.64M | else { |
409 | | // Allocate a new node. |
410 | 2.64M | V = getAllocator().Allocate<NodeTy>(); |
411 | 2.64M | } |
412 | | |
413 | 3.20M | ++NumNodes; |
414 | 3.20M | new (V) NodeTy(L, State, NumNodes, IsSink); |
415 | | |
416 | 3.20M | if (ReclaimNodeInterval) |
417 | 3.20M | ChangedNodes.push_back(V); |
418 | | |
419 | | // Insert the node into the node set and return it. |
420 | 3.20M | Nodes.InsertNode(V, InsertPos); |
421 | | |
422 | 3.20M | if (IsNew) *IsNew = true; |
423 | 3.20M | } |
424 | 9.13k | else |
425 | 9.13k | if (IsNew) *IsNew = false; |
426 | | |
427 | 3.21M | return V; |
428 | 3.21M | } |
429 | | |
430 | | ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L, |
431 | | ProgramStateRef State, |
432 | | int64_t Id, |
433 | 7.54M | bool IsSink) { |
434 | 7.54M | NodeTy *V = getAllocator().Allocate<NodeTy>(); |
435 | 7.54M | new (V) NodeTy(L, State, Id, IsSink); |
436 | 7.54M | return V; |
437 | 7.54M | } |
438 | | |
439 | | std::unique_ptr<ExplodedGraph> |
440 | | ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, |
441 | | InterExplodedGraphMap *ForwardMap, |
442 | 20.9k | InterExplodedGraphMap *InverseMap) const { |
443 | 20.9k | if (Nodes.empty()) |
444 | 0 | return nullptr; |
445 | | |
446 | 20.9k | using Pass1Ty = llvm::DenseSet<const ExplodedNode *>; |
447 | 20.9k | Pass1Ty Pass1; |
448 | | |
449 | 20.9k | using Pass2Ty = InterExplodedGraphMap; |
450 | 20.9k | InterExplodedGraphMap Pass2Scratch; |
451 | 20.9k | Pass2Ty &Pass2 = ForwardMap ? *ForwardMap20.9k : Pass2Scratch1 ; |
452 | | |
453 | 20.9k | SmallVector<const ExplodedNode*, 10> WL1, WL2; |
454 | | |
455 | | // ===- Pass 1 (reverse DFS) -=== |
456 | 20.9k | for (const auto Sink : Sinks) |
457 | 22.9k | if (Sink) |
458 | 22.9k | WL1.push_back(Sink); |
459 | | |
460 | | // Process the first worklist until it is empty. |
461 | 4.15M | while (!WL1.empty()) { |
462 | 4.13M | const ExplodedNode *N = WL1.pop_back_val(); |
463 | | |
464 | | // Have we already visited this node? If so, continue to the next one. |
465 | 4.13M | if (!Pass1.insert(N).second) |
466 | 15.6k | continue; |
467 | | |
468 | | // If this is a root enqueue it to the second worklist. |
469 | 4.12M | if (N->Preds.empty()) { |
470 | 20.9k | WL2.push_back(N); |
471 | 20.9k | continue; |
472 | 20.9k | } |
473 | | |
474 | | // Visit our predecessors and enqueue them. |
475 | 4.10M | WL1.append(N->Preds.begin(), N->Preds.end()); |
476 | 4.10M | } |
477 | | |
478 | | // We didn't hit a root? Return with a null pointer for the new graph. |
479 | 20.9k | if (WL2.empty()) |
480 | 0 | return nullptr; |
481 | | |
482 | | // Create an empty graph. |
483 | 20.9k | std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph(); |
484 | | |
485 | | // ===- Pass 2 (forward DFS to construct the new graph) -=== |
486 | 4.14M | while (!WL2.empty()) { |
487 | 4.12M | const ExplodedNode *N = WL2.pop_back_val(); |
488 | | |
489 | | // Skip this node if we have already processed it. |
490 | 4.12M | if (Pass2.contains(N)) |
491 | 10 | continue; |
492 | | |
493 | | // Create the corresponding node in the new graph and record the mapping |
494 | | // from the old node to the new node. |
495 | 4.12M | ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, |
496 | 4.12M | N->getID(), N->isSink()); |
497 | 4.12M | Pass2[N] = NewN; |
498 | | |
499 | | // Also record the reverse mapping from the new node to the old node. |
500 | 4.12M | if (InverseMap) (*InverseMap)[NewN] = N0 ; |
501 | | |
502 | | // If this node is a root, designate it as such in the graph. |
503 | 4.12M | if (N->Preds.empty()) |
504 | 20.9k | G->addRoot(NewN); |
505 | | |
506 | | // In the case that some of the intended predecessors of NewN have already |
507 | | // been created, we should hook them up as predecessors. |
508 | | |
509 | | // Walk through the predecessors of 'N' and hook up their corresponding |
510 | | // nodes in the new graph (if any) to the freshly created node. |
511 | 4.12M | for (const ExplodedNode *Pred : N->Preds) { |
512 | 4.11M | Pass2Ty::iterator PI = Pass2.find(Pred); |
513 | 4.11M | if (PI == Pass2.end()) |
514 | 13.6k | continue; |
515 | | |
516 | 4.10M | NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G); |
517 | 4.10M | } |
518 | | |
519 | | // In the case that some of the intended successors of NewN have already |
520 | | // been created, we should hook them up as successors. Otherwise, enqueue |
521 | | // the new nodes from the original graph that should have nodes created |
522 | | // in the new graph. |
523 | 4.14M | for (const ExplodedNode *Succ : N->Succs) { |
524 | 4.14M | Pass2Ty::iterator PI = Pass2.find(Succ); |
525 | 4.14M | if (PI != Pass2.end()) { |
526 | 13.6k | const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G); |
527 | 13.6k | continue; |
528 | 13.6k | } |
529 | | |
530 | | // Enqueue nodes to the worklist that were marked during pass 1. |
531 | 4.13M | if (Pass1.count(Succ)) |
532 | 4.10M | WL2.push_back(Succ); |
533 | 4.13M | } |
534 | 4.12M | } |
535 | | |
536 | 20.9k | return G; |
537 | 20.9k | } |