/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// |
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 a generic engine for intraprocedural, path-sensitive, |
10 | | // dataflow analysis via graph reachability engine. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" |
15 | | #include "clang/AST/Expr.h" |
16 | | #include "clang/AST/ExprCXX.h" |
17 | | #include "clang/AST/Stmt.h" |
18 | | #include "clang/AST/StmtCXX.h" |
19 | | #include "clang/Analysis/AnalysisDeclContext.h" |
20 | | #include "clang/Analysis/CFG.h" |
21 | | #include "clang/Analysis/ProgramPoint.h" |
22 | | #include "clang/Basic/LLVM.h" |
23 | | #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" |
24 | | #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" |
25 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
26 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
27 | | #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" |
28 | | #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" |
29 | | #include "llvm/ADT/STLExtras.h" |
30 | | #include "llvm/ADT/Statistic.h" |
31 | | #include "llvm/Support/Casting.h" |
32 | | #include "llvm/Support/ErrorHandling.h" |
33 | | #include <algorithm> |
34 | | #include <cassert> |
35 | | #include <memory> |
36 | | #include <optional> |
37 | | #include <utility> |
38 | | |
39 | | using namespace clang; |
40 | | using namespace ento; |
41 | | |
42 | | #define DEBUG_TYPE "CoreEngine" |
43 | | |
44 | | STATISTIC(NumSteps, |
45 | | "The # of steps executed."); |
46 | | STATISTIC(NumSTUSteps, "The # of STU steps executed."); |
47 | | STATISTIC(NumCTUSteps, "The # of CTU steps executed."); |
48 | | STATISTIC(NumReachedMaxSteps, |
49 | | "The # of times we reached the max number of steps."); |
50 | | STATISTIC(NumPathsExplored, |
51 | | "The # of paths explored by the analyzer."); |
52 | | |
53 | | //===----------------------------------------------------------------------===// |
54 | | // Core analysis engine. |
55 | | //===----------------------------------------------------------------------===// |
56 | | |
57 | 16.2k | static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) { |
58 | 16.2k | switch (Opts.getExplorationStrategy()) { |
59 | 333 | case ExplorationStrategyKind::DFS: |
60 | 333 | return WorkList::makeDFS(); |
61 | 0 | case ExplorationStrategyKind::BFS: |
62 | 0 | return WorkList::makeBFS(); |
63 | 0 | case ExplorationStrategyKind::BFSBlockDFSContents: |
64 | 0 | return WorkList::makeBFSBlockDFSContents(); |
65 | 4 | case ExplorationStrategyKind::UnexploredFirst: |
66 | 4 | return WorkList::makeUnexploredFirst(); |
67 | 15.9k | case ExplorationStrategyKind::UnexploredFirstQueue: |
68 | 15.9k | return WorkList::makeUnexploredFirstPriorityQueue(); |
69 | 0 | case ExplorationStrategyKind::UnexploredFirstLocationQueue: |
70 | 0 | return WorkList::makeUnexploredFirstPriorityLocationQueue(); |
71 | 16.2k | } |
72 | 0 | llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind"); |
73 | 0 | } |
74 | | |
75 | | CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, |
76 | | AnalyzerOptions &Opts) |
77 | 16.2k | : ExprEng(exprengine), WList(generateWorkList(Opts)), |
78 | 16.2k | CTUWList(Opts.IsNaiveCTUEnabled ? generateWorkList(Opts)52 : nullptr16.1k ), |
79 | 16.2k | BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} |
80 | | |
81 | 1.99M | void CoreEngine::setBlockCounter(BlockCounter C) { |
82 | 1.99M | WList->setBlockCounter(C); |
83 | 1.99M | if (CTUWList) |
84 | 3.05k | CTUWList->setBlockCounter(C); |
85 | 1.99M | } |
86 | | |
87 | | /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. |
88 | | bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps, |
89 | 16.1k | ProgramStateRef InitState) { |
90 | 16.1k | if (G.num_roots() == 0) { // Initialize the analysis by constructing |
91 | | // the root if none exists. |
92 | | |
93 | 16.1k | const CFGBlock *Entry = &(L->getCFG()->getEntry()); |
94 | | |
95 | 16.1k | assert(Entry->empty() && "Entry block must be empty."); |
96 | | |
97 | 16.1k | assert(Entry->succ_size() == 1 && "Entry block must have 1 successor."); |
98 | | |
99 | | // Mark the entry block as visited. |
100 | 16.1k | FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), |
101 | 16.1k | L->getDecl(), |
102 | 16.1k | L->getCFG()->getNumBlockIDs()); |
103 | | |
104 | | // Get the solitary successor. |
105 | 16.1k | const CFGBlock *Succ = *(Entry->succ_begin()); |
106 | | |
107 | | // Construct an edge representing the |
108 | | // starting location in the function. |
109 | 16.1k | BlockEdge StartLoc(Entry, Succ, L); |
110 | | |
111 | | // Set the current block counter to being empty. |
112 | 16.1k | setBlockCounter(BCounterFactory.GetEmptyCounter()); |
113 | | |
114 | 16.1k | if (!InitState) |
115 | 16.1k | InitState = ExprEng.getInitialState(L); |
116 | | |
117 | 16.1k | bool IsNew; |
118 | 16.1k | ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); |
119 | 16.1k | assert(IsNew); |
120 | 16.1k | G.addRoot(Node); |
121 | | |
122 | 16.1k | NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); |
123 | 16.1k | ExplodedNodeSet DstBegin; |
124 | 16.1k | ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); |
125 | | |
126 | 16.1k | enqueue(DstBegin); |
127 | 16.1k | } |
128 | | |
129 | | // Check if we have a steps limit |
130 | 16.1k | bool UnlimitedSteps = MaxSteps == 0; |
131 | | |
132 | | // Cap our pre-reservation in the event that the user specifies |
133 | | // a very large number of maximum steps. |
134 | 16.1k | const unsigned PreReservationCap = 4000000; |
135 | 16.1k | if(!UnlimitedSteps) |
136 | 16.1k | G.reserve(std::min(MaxSteps, PreReservationCap)); |
137 | | |
138 | 16.2k | auto ProcessWList = [this, UnlimitedSteps](unsigned MaxSteps) { |
139 | 16.2k | unsigned Steps = MaxSteps; |
140 | 1.84M | while (WList->hasWork()) { |
141 | 1.82M | if (!UnlimitedSteps) { |
142 | 1.82M | if (Steps == 0) { |
143 | 11 | NumReachedMaxSteps++; |
144 | 11 | break; |
145 | 11 | } |
146 | 1.82M | --Steps; |
147 | 1.82M | } |
148 | | |
149 | 1.82M | NumSteps++; |
150 | | |
151 | 1.82M | const WorkListUnit &WU = WList->dequeue(); |
152 | | |
153 | | // Set the current block counter. |
154 | 1.82M | setBlockCounter(WU.getBlockCounter()); |
155 | | |
156 | | // Retrieve the node. |
157 | 1.82M | ExplodedNode *Node = WU.getNode(); |
158 | | |
159 | 1.82M | dispatchWorkItem(Node, Node->getLocation(), WU); |
160 | 1.82M | } |
161 | 16.2k | return MaxSteps - Steps; |
162 | 16.2k | }; |
163 | 16.1k | const unsigned STUSteps = ProcessWList(MaxSteps); |
164 | | |
165 | 16.1k | if (CTUWList) { |
166 | 52 | NumSTUSteps += STUSteps; |
167 | 52 | const unsigned MinCTUSteps = |
168 | 52 | this->ExprEng.getAnalysisManager().options.CTUMaxNodesMin; |
169 | 52 | const unsigned Pct = |
170 | 52 | this->ExprEng.getAnalysisManager().options.CTUMaxNodesPercentage; |
171 | 52 | unsigned MaxCTUSteps = std::max(STUSteps * Pct / 100, MinCTUSteps); |
172 | | |
173 | 52 | WList = std::move(CTUWList); |
174 | 52 | const unsigned CTUSteps = ProcessWList(MaxCTUSteps); |
175 | 52 | NumCTUSteps += CTUSteps; |
176 | 52 | } |
177 | | |
178 | 16.1k | ExprEng.processEndWorklist(); |
179 | 16.1k | return WList->hasWork(); |
180 | 16.1k | } |
181 | | |
182 | | void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, |
183 | 1.82M | const WorkListUnit& WU) { |
184 | | // Dispatch on the location type. |
185 | 1.82M | switch (Loc.getKind()) { |
186 | 237k | case ProgramPoint::BlockEdgeKind: |
187 | 237k | HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred); |
188 | 237k | break; |
189 | | |
190 | 152k | case ProgramPoint::BlockEntranceKind: |
191 | 152k | HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred); |
192 | 152k | break; |
193 | | |
194 | 0 | case ProgramPoint::BlockExitKind: |
195 | 0 | assert(false && "BlockExit location never occur in forward analysis."); |
196 | 0 | break; |
197 | | |
198 | 35.1k | case ProgramPoint::CallEnterKind: |
199 | 35.1k | HandleCallEnter(Loc.castAs<CallEnter>(), Pred); |
200 | 35.1k | break; |
201 | | |
202 | 42.5k | case ProgramPoint::CallExitBeginKind: |
203 | 42.5k | ExprEng.processCallExit(Pred); |
204 | 42.5k | break; |
205 | | |
206 | 38 | case ProgramPoint::EpsilonKind: { |
207 | 38 | assert(Pred->hasSinglePred() && |
208 | 38 | "Assume epsilon has exactly one predecessor by construction"); |
209 | 38 | ExplodedNode *PNode = Pred->getFirstPred(); |
210 | 38 | dispatchWorkItem(Pred, PNode->getLocation(), WU); |
211 | 38 | break; |
212 | 38 | } |
213 | 1.36M | default: |
214 | 1.36M | assert(Loc.getAs<PostStmt>() || |
215 | 1.36M | Loc.getAs<PostInitializer>() || |
216 | 1.36M | Loc.getAs<PostImplicitCall>() || |
217 | 1.36M | Loc.getAs<CallExitEnd>() || |
218 | 1.36M | Loc.getAs<LoopExit>() || |
219 | 1.36M | Loc.getAs<PostAllocatorCall>()); |
220 | 1.36M | HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); |
221 | 1.36M | break; |
222 | 1.82M | } |
223 | 1.82M | } |
224 | | |
225 | | bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, |
226 | | unsigned Steps, |
227 | | ProgramStateRef InitState, |
228 | 0 | ExplodedNodeSet &Dst) { |
229 | 0 | bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); |
230 | 0 | for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; |
231 | 0 | ++I) { |
232 | 0 | Dst.Add(*I); |
233 | 0 | } |
234 | 0 | return DidNotFinish; |
235 | 0 | } |
236 | | |
237 | 237k | void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { |
238 | 237k | const CFGBlock *Blk = L.getDst(); |
239 | 237k | NodeBuilderContext BuilderCtx(*this, Blk, Pred); |
240 | | |
241 | | // Mark this block as visited. |
242 | 237k | const LocationContext *LC = Pred->getLocationContext(); |
243 | 237k | FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(), |
244 | 237k | LC->getDecl(), |
245 | 237k | LC->getCFG()->getNumBlockIDs()); |
246 | | |
247 | | // Display a prunable path note to the user if it's a virtual bases branch |
248 | | // and we're taking the path that skips virtual base constructors. |
249 | 237k | if (L.getSrc()->getTerminator().isVirtualBaseBranch() && |
250 | 237k | L.getDst() == *L.getSrc()->succ_begin()155 ) { |
251 | 71 | ProgramPoint P = L.withTag(getDataTags().make<NoteTag>( |
252 | 110 | [](BugReporterContext &, PathSensitiveBugReport &) -> std::string { |
253 | | // TODO: Just call out the name of the most derived class |
254 | | // when we know it. |
255 | 110 | return "Virtual base initialization skipped because " |
256 | 110 | "it has already been handled by the most derived class"; |
257 | 110 | }, |
258 | 71 | /*IsPrunable=*/true)); |
259 | | // Perform the transition. |
260 | 71 | ExplodedNodeSet Dst; |
261 | 71 | NodeBuilder Bldr(Pred, Dst, BuilderCtx); |
262 | 71 | Pred = Bldr.generateNode(P, Pred->getState(), Pred); |
263 | 71 | if (!Pred) |
264 | 0 | return; |
265 | 71 | } |
266 | | |
267 | | // Check if we are entering the EXIT block. |
268 | 237k | if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { |
269 | 68.0k | assert(L.getLocationContext()->getCFG()->getExit().empty() && |
270 | 68.0k | "EXIT block cannot contain Stmts."); |
271 | | |
272 | | // Get return statement.. |
273 | 68.0k | const ReturnStmt *RS = nullptr; |
274 | 68.0k | if (!L.getSrc()->empty()) { |
275 | 63.4k | CFGElement LastElement = L.getSrc()->back(); |
276 | 63.4k | if (std::optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) { |
277 | 56.8k | RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()); |
278 | 56.8k | } else if (std::optional<CFGAutomaticObjDtor> 6.61k AutoDtor6.61k = |
279 | 6.61k | LastElement.getAs<CFGAutomaticObjDtor>()) { |
280 | 328 | RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt()); |
281 | 328 | } |
282 | 63.4k | } |
283 | | |
284 | | // Process the final state transition. |
285 | 68.0k | ExprEng.processEndOfFunction(BuilderCtx, Pred, RS); |
286 | | |
287 | | // This path is done. Don't enqueue any more nodes. |
288 | 68.0k | return; |
289 | 68.0k | } |
290 | | |
291 | | // Call into the ExprEngine to process entering the CFGBlock. |
292 | 169k | ExplodedNodeSet dstNodes; |
293 | 169k | BlockEntrance BE(Blk, Pred->getLocationContext()); |
294 | 169k | NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); |
295 | 169k | ExprEng.processCFGBlockEntrance(L, nodeBuilder, Pred); |
296 | | |
297 | | // Auto-generate a node. |
298 | 169k | if (!nodeBuilder.hasGeneratedNodes()) { |
299 | 166k | nodeBuilder.generateNode(Pred->State, Pred); |
300 | 166k | } |
301 | | |
302 | | // Enqueue nodes onto the worklist. |
303 | 169k | enqueue(dstNodes); |
304 | 169k | } |
305 | | |
306 | | void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, |
307 | 152k | ExplodedNode *Pred) { |
308 | | // Increment the block counter. |
309 | 152k | const LocationContext *LC = Pred->getLocationContext(); |
310 | 152k | unsigned BlockId = L.getBlock()->getBlockID(); |
311 | 152k | BlockCounter Counter = WList->getBlockCounter(); |
312 | 152k | Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(), |
313 | 152k | BlockId); |
314 | 152k | setBlockCounter(Counter); |
315 | | |
316 | | // Process the entrance of the block. |
317 | 152k | if (std::optional<CFGElement> E = L.getFirstElement()) { |
318 | 139k | NodeBuilderContext Ctx(*this, L.getBlock(), Pred); |
319 | 139k | ExprEng.processCFGElement(*E, Pred, 0, &Ctx); |
320 | 139k | } else |
321 | 13.3k | HandleBlockExit(L.getBlock(), Pred); |
322 | 152k | } |
323 | | |
324 | 155k | void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { |
325 | 155k | if (const Stmt *Term = B->getTerminatorStmt()) { |
326 | 62.3k | switch (Term->getStmtClass()) { |
327 | 0 | default: |
328 | 0 | llvm_unreachable("Analysis for this terminator not implemented."); |
329 | |
|
330 | 601 | case Stmt::CXXBindTemporaryExprClass: |
331 | 601 | HandleCleanupTemporaryBranch( |
332 | 601 | cast<CXXBindTemporaryExpr>(Term), B, Pred); |
333 | 601 | return; |
334 | | |
335 | | // Model static initializers. |
336 | 212 | case Stmt::DeclStmtClass: |
337 | 212 | HandleStaticInit(cast<DeclStmt>(Term), B, Pred); |
338 | 212 | return; |
339 | | |
340 | 20.8k | case Stmt::BinaryOperatorClass: // '&&' and '||' |
341 | 20.8k | HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); |
342 | 20.8k | return; |
343 | | |
344 | 27 | case Stmt::BinaryConditionalOperatorClass: |
345 | 4.79k | case Stmt::ConditionalOperatorClass: |
346 | 4.79k | HandleBranch(cast<AbstractConditionalOperator>(Term)->getCond(), |
347 | 4.79k | Term, B, Pred); |
348 | 4.79k | return; |
349 | | |
350 | | // FIXME: Use constant-folding in CFG construction to simplify this |
351 | | // case. |
352 | | |
353 | 0 | case Stmt::ChooseExprClass: |
354 | 0 | HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); |
355 | 0 | return; |
356 | | |
357 | 0 | case Stmt::CXXTryStmtClass: |
358 | | // Generate a node for each of the successors. |
359 | | // Our logic for EH analysis can certainly be improved. |
360 | 0 | for (CFGBlock::const_succ_iterator it = B->succ_begin(), |
361 | 0 | et = B->succ_end(); it != et; ++it) { |
362 | 0 | if (const CFGBlock *succ = *it) { |
363 | 0 | generateNode(BlockEdge(B, succ, Pred->getLocationContext()), |
364 | 0 | Pred->State, Pred); |
365 | 0 | } |
366 | 0 | } |
367 | 0 | return; |
368 | | |
369 | 263 | case Stmt::DoStmtClass: |
370 | 263 | HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); |
371 | 263 | return; |
372 | | |
373 | 187 | case Stmt::CXXForRangeStmtClass: |
374 | 187 | HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred); |
375 | 187 | return; |
376 | | |
377 | 6.12k | case Stmt::ForStmtClass: |
378 | 6.12k | HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); |
379 | 6.12k | return; |
380 | | |
381 | 1 | case Stmt::SEHLeaveStmtClass: |
382 | 20 | case Stmt::ContinueStmtClass: |
383 | 554 | case Stmt::BreakStmtClass: |
384 | 583 | case Stmt::GotoStmtClass: |
385 | 583 | break; |
386 | | |
387 | 17.8k | case Stmt::IfStmtClass: |
388 | 17.8k | HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred); |
389 | 17.8k | return; |
390 | | |
391 | 8 | case Stmt::IndirectGotoStmtClass: { |
392 | | // Only 1 successor: the indirect goto dispatch block. |
393 | 8 | assert(B->succ_size() == 1); |
394 | | |
395 | 8 | IndirectGotoNodeBuilder |
396 | 8 | builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), |
397 | 8 | *(B->succ_begin()), this); |
398 | | |
399 | 8 | ExprEng.processIndirectGoto(builder); |
400 | 8 | return; |
401 | 8 | } |
402 | | |
403 | 448 | case Stmt::ObjCForCollectionStmtClass: |
404 | | // In the case of ObjCForCollectionStmt, it appears twice in a CFG: |
405 | | // |
406 | | // (1) inside a basic block, which represents the binding of the |
407 | | // 'element' variable to a value. |
408 | | // (2) in a terminator, which represents the branch. |
409 | | // |
410 | | // For (1), ExprEngine will bind a value (i.e., 0 or 1) indicating |
411 | | // whether or not collection contains any more elements. We cannot |
412 | | // just test to see if the element is nil because a container can |
413 | | // contain nil elements. |
414 | 448 | HandleBranch(Term, Term, B, Pred); |
415 | 448 | return; |
416 | | |
417 | 356 | case Stmt::SwitchStmtClass: { |
418 | 356 | SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), |
419 | 356 | this); |
420 | | |
421 | 356 | ExprEng.processSwitch(builder); |
422 | 356 | return; |
423 | 8 | } |
424 | | |
425 | 9.99k | case Stmt::WhileStmtClass: |
426 | 9.99k | HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); |
427 | 9.99k | return; |
428 | | |
429 | 1 | case Stmt::GCCAsmStmtClass: |
430 | 1 | assert(cast<GCCAsmStmt>(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels"); |
431 | | // TODO: Handle jumping to labels |
432 | 1 | return; |
433 | 62.3k | } |
434 | 62.3k | } |
435 | | |
436 | 94.1k | if (B->getTerminator().isVirtualBaseBranch()) { |
437 | 155 | HandleVirtualBaseBranch(B, Pred); |
438 | 155 | return; |
439 | 155 | } |
440 | | |
441 | 94.0k | assert(B->succ_size() == 1 && |
442 | 94.0k | "Blocks with no terminator should have at most 1 successor."); |
443 | | |
444 | 94.0k | generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), |
445 | 94.0k | Pred->State, Pred); |
446 | 94.0k | } |
447 | | |
448 | 35.1k | void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) { |
449 | 35.1k | NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred); |
450 | 35.1k | ExprEng.processCallEnter(BuilderCtx, CE, Pred); |
451 | 35.1k | } |
452 | | |
453 | | void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, |
454 | 60.6k | const CFGBlock * B, ExplodedNode *Pred) { |
455 | 60.6k | assert(B->succ_size() == 2); |
456 | 60.6k | NodeBuilderContext Ctx(*this, B, Pred); |
457 | 60.6k | ExplodedNodeSet Dst; |
458 | 60.6k | ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()), |
459 | 60.6k | *(B->succ_begin() + 1)); |
460 | | // Enqueue the new frontier onto the worklist. |
461 | 60.6k | enqueue(Dst); |
462 | 60.6k | } |
463 | | |
464 | | void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, |
465 | | const CFGBlock *B, |
466 | 601 | ExplodedNode *Pred) { |
467 | 601 | assert(B->succ_size() == 2); |
468 | 601 | NodeBuilderContext Ctx(*this, B, Pred); |
469 | 601 | ExplodedNodeSet Dst; |
470 | 601 | ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), |
471 | 601 | *(B->succ_begin() + 1)); |
472 | | // Enqueue the new frontier onto the worklist. |
473 | 601 | enqueue(Dst); |
474 | 601 | } |
475 | | |
476 | | void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, |
477 | 212 | ExplodedNode *Pred) { |
478 | 212 | assert(B->succ_size() == 2); |
479 | 212 | NodeBuilderContext Ctx(*this, B, Pred); |
480 | 212 | ExplodedNodeSet Dst; |
481 | 212 | ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst, |
482 | 212 | *(B->succ_begin()), *(B->succ_begin()+1)); |
483 | | // Enqueue the new frontier onto the worklist. |
484 | 212 | enqueue(Dst); |
485 | 212 | } |
486 | | |
487 | | void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, |
488 | 1.36M | ExplodedNode *Pred) { |
489 | 1.36M | assert(B); |
490 | 1.36M | assert(!B->empty()); |
491 | | |
492 | 1.36M | if (StmtIdx == B->size()) |
493 | 142k | HandleBlockExit(B, Pred); |
494 | 1.21M | else { |
495 | 1.21M | NodeBuilderContext Ctx(*this, B, Pred); |
496 | 1.21M | ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); |
497 | 1.21M | } |
498 | 1.36M | } |
499 | | |
500 | | void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B, |
501 | 155 | ExplodedNode *Pred) { |
502 | 155 | const LocationContext *LCtx = Pred->getLocationContext(); |
503 | 155 | if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>( |
504 | 155 | LCtx->getStackFrame()->getCallSite())) { |
505 | 133 | switch (CallerCtor->getConstructionKind()) { |
506 | 60 | case CXXConstructExpr::CK_NonVirtualBase: |
507 | 71 | case CXXConstructExpr::CK_VirtualBase: { |
508 | 71 | BlockEdge Loc(B, *B->succ_begin(), LCtx); |
509 | 71 | HandleBlockEdge(Loc, Pred); |
510 | 71 | return; |
511 | 60 | } |
512 | 62 | default: |
513 | 62 | break; |
514 | 133 | } |
515 | 133 | } |
516 | | |
517 | | // We either don't see a parent stack frame because we're in the top frame, |
518 | | // or the parent stack frame doesn't initialize our virtual bases. |
519 | 84 | BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx); |
520 | 84 | HandleBlockEdge(Loc, Pred); |
521 | 84 | } |
522 | | |
523 | | /// generateNode - Utility method to generate nodes, hook up successors, |
524 | | /// and add nodes to the worklist. |
525 | | void CoreEngine::generateNode(const ProgramPoint &Loc, |
526 | | ProgramStateRef State, |
527 | 94.0k | ExplodedNode *Pred) { |
528 | 94.0k | bool IsNew; |
529 | 94.0k | ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew); |
530 | | |
531 | 94.0k | if (Pred) |
532 | 94.0k | Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor. |
533 | 0 | else { |
534 | 0 | assert(IsNew); |
535 | 0 | G.addRoot(Node); // 'Node' has no predecessor. Make it a root. |
536 | 0 | } |
537 | | |
538 | | // Only add 'Node' to the worklist if it was freshly generated. |
539 | 94.0k | if (IsNew) WList->enqueue(Node)93.9k ; |
540 | 94.0k | } |
541 | | |
542 | | void CoreEngine::enqueueStmtNode(ExplodedNode *N, |
543 | 1.32M | const CFGBlock *Block, unsigned Idx) { |
544 | 1.32M | assert(Block); |
545 | 1.32M | assert(!N->isSink()); |
546 | | |
547 | | // Check if this node entered a callee. |
548 | 1.32M | if (N->getLocation().getAs<CallEnter>()) { |
549 | | // Still use the index of the CallExpr. It's needed to create the callee |
550 | | // StackFrameContext. |
551 | 0 | WList->enqueue(N, Block, Idx); |
552 | 0 | return; |
553 | 0 | } |
554 | | |
555 | | // Do not create extra nodes. Move to the next CFG element. |
556 | 1.32M | if (N->getLocation().getAs<PostInitializer>() || |
557 | 1.32M | N->getLocation().getAs<PostImplicitCall>()1.30M || |
558 | 1.32M | N->getLocation().getAs<LoopExit>()1.30M ) { |
559 | 13.3k | WList->enqueue(N, Block, Idx+1); |
560 | 13.3k | return; |
561 | 13.3k | } |
562 | | |
563 | 1.30M | if (N->getLocation().getAs<EpsilonPoint>()) { |
564 | 38 | WList->enqueue(N, Block, Idx); |
565 | 38 | return; |
566 | 38 | } |
567 | | |
568 | 1.30M | if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) { |
569 | 845 | WList->enqueue(N, Block, Idx+1); |
570 | 845 | return; |
571 | 845 | } |
572 | | |
573 | | // At this point, we know we're processing a normal statement. |
574 | 1.30M | CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); |
575 | 1.30M | PostStmt Loc(CS.getStmt(), N->getLocationContext()); |
576 | | |
577 | 1.30M | if (Loc == N->getLocation().withTag(nullptr)) { |
578 | | // Note: 'N' should be a fresh node because otherwise it shouldn't be |
579 | | // a member of Deferred. |
580 | 512k | WList->enqueue(N, Block, Idx+1); |
581 | 512k | return; |
582 | 512k | } |
583 | | |
584 | 792k | bool IsNew; |
585 | 792k | ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew); |
586 | 792k | Succ->addPredecessor(N, G); |
587 | | |
588 | 792k | if (IsNew) |
589 | 792k | WList->enqueue(Succ, Block, Idx+1); |
590 | 792k | } |
591 | | |
592 | | ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, |
593 | 42.5k | const ReturnStmt *RS) { |
594 | | // Create a CallExitBegin node and enqueue it. |
595 | 42.5k | const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext()); |
596 | | |
597 | | // Use the callee location context. |
598 | 42.5k | CallExitBegin Loc(LocCtx, RS); |
599 | | |
600 | 42.5k | bool isNew; |
601 | 42.5k | ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew); |
602 | 42.5k | Node->addPredecessor(N, G); |
603 | 42.5k | return isNew ? Node : nullptr0 ; |
604 | 42.5k | } |
605 | | |
606 | 281k | void CoreEngine::enqueue(ExplodedNodeSet &Set) { |
607 | 281k | for (const auto I : Set) |
608 | 309k | WList->enqueue(I); |
609 | 281k | } |
610 | | |
611 | | void CoreEngine::enqueue(ExplodedNodeSet &Set, |
612 | 1.35M | const CFGBlock *Block, unsigned Idx) { |
613 | 1.35M | for (const auto I : Set) |
614 | 1.32M | enqueueStmtNode(I, Block, Idx); |
615 | 1.35M | } |
616 | | |
617 | 68.0k | void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { |
618 | 68.0k | for (auto *I : Set) { |
619 | | // If we are in an inlined call, generate CallExitBegin node. |
620 | 66.0k | if (I->getLocationContext()->getParent()) { |
621 | 42.5k | I = generateCallExitBeginNode(I, RS); |
622 | 42.5k | if (I) |
623 | 42.5k | WList->enqueue(I); |
624 | 42.5k | } else { |
625 | | // TODO: We should run remove dead bindings here. |
626 | 23.5k | G.addEndOfPath(I); |
627 | 23.5k | NumPathsExplored++; |
628 | 23.5k | } |
629 | 66.0k | } |
630 | 68.0k | } |
631 | | |
632 | 0 | void NodeBuilder::anchor() {} |
633 | | |
634 | | ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, |
635 | | ProgramStateRef State, |
636 | | ExplodedNode *FromN, |
637 | 2.12M | bool MarkAsSink) { |
638 | 2.12M | HasGeneratedNodes = true; |
639 | 2.12M | bool IsNew; |
640 | 2.12M | ExplodedNode *N = C.Eng.G.getNode(Loc, State, MarkAsSink, &IsNew); |
641 | 2.12M | N->addPredecessor(FromN, C.Eng.G); |
642 | 2.12M | Frontier.erase(FromN); |
643 | | |
644 | 2.12M | if (!IsNew) |
645 | 9.19k | return nullptr; |
646 | | |
647 | 2.11M | if (!MarkAsSink) |
648 | 2.10M | Frontier.Add(N); |
649 | | |
650 | 2.11M | return N; |
651 | 2.12M | } |
652 | | |
653 | 0 | void NodeBuilderWithSinks::anchor() {} |
654 | | |
655 | 3.64M | StmtNodeBuilder::~StmtNodeBuilder() { |
656 | 3.64M | if (EnclosingBldr) |
657 | 0 | for (const auto I : Frontier) |
658 | 0 | EnclosingBldr->addNodes(I); |
659 | 3.64M | } |
660 | | |
661 | 0 | void BranchNodeBuilder::anchor() {} |
662 | | |
663 | | ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, |
664 | | bool branch, |
665 | 90.8k | ExplodedNode *NodePred) { |
666 | | // If the branch has been marked infeasible we should not generate a node. |
667 | 90.8k | if (!isFeasible(branch)) |
668 | 0 | return nullptr; |
669 | | |
670 | 90.8k | ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT46.1k :DstF44.7k , |
671 | 90.8k | NodePred->getLocationContext()); |
672 | 90.8k | ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred); |
673 | 90.8k | return Succ; |
674 | 90.8k | } |
675 | | |
676 | | ExplodedNode* |
677 | | IndirectGotoNodeBuilder::generateNode(const iterator &I, |
678 | | ProgramStateRef St, |
679 | 8 | bool IsSink) { |
680 | 8 | bool IsNew; |
681 | 8 | ExplodedNode *Succ = |
682 | 8 | Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), |
683 | 8 | St, IsSink, &IsNew); |
684 | 8 | Succ->addPredecessor(Pred, Eng.G); |
685 | | |
686 | 8 | if (!IsNew) |
687 | 0 | return nullptr; |
688 | | |
689 | 8 | if (!IsSink) |
690 | 8 | Eng.WList->enqueue(Succ); |
691 | | |
692 | 8 | return Succ; |
693 | 8 | } |
694 | | |
695 | | ExplodedNode* |
696 | | SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, |
697 | 762 | ProgramStateRef St) { |
698 | 762 | bool IsNew; |
699 | 762 | ExplodedNode *Succ = |
700 | 762 | Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), |
701 | 762 | St, false, &IsNew); |
702 | 762 | Succ->addPredecessor(Pred, Eng.G); |
703 | 762 | if (!IsNew) |
704 | 0 | return nullptr; |
705 | | |
706 | 762 | Eng.WList->enqueue(Succ); |
707 | 762 | return Succ; |
708 | 762 | } |
709 | | |
710 | | ExplodedNode* |
711 | | SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, |
712 | 224 | bool IsSink) { |
713 | | // Get the block for the default case. |
714 | 224 | assert(Src->succ_rbegin() != Src->succ_rend()); |
715 | 224 | CFGBlock *DefaultBlock = *Src->succ_rbegin(); |
716 | | |
717 | | // Basic correctness check for default blocks that are unreachable and not |
718 | | // caught by earlier stages. |
719 | 224 | if (!DefaultBlock) |
720 | 0 | return nullptr; |
721 | | |
722 | 224 | bool IsNew; |
723 | 224 | ExplodedNode *Succ = |
724 | 224 | Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), |
725 | 224 | St, IsSink, &IsNew); |
726 | 224 | Succ->addPredecessor(Pred, Eng.G); |
727 | | |
728 | 224 | if (!IsNew) |
729 | 0 | return nullptr; |
730 | | |
731 | 224 | if (!IsSink) |
732 | 224 | Eng.WList->enqueue(Succ); |
733 | | |
734 | 224 | return Succ; |
735 | 224 | } |