clang 23.0.0git
ExplodedGraph.h
Go to the documentation of this file.
1//===- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -----*- C++ -*-===//
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// See "Precise interprocedural dataflow analysis via graph reachability"
12// by Reps, Horwitz, and Sagiv
13// (http://portal.acm.org/citation.cfm?id=199462) for the definition of an
14// exploded graph.
15//
16//===----------------------------------------------------------------------===//
17
18#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H
19#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H
20
24#include "clang/Basic/LLVM.h"
28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/DepthFirstIterator.h"
31#include "llvm/ADT/FoldingSet.h"
32#include "llvm/ADT/GraphTraits.h"
33#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/SetVector.h"
35#include "llvm/ADT/iterator_range.h"
36#include "llvm/Support/Allocator.h"
37#include "llvm/Support/Compiler.h"
38#include <cassert>
39#include <cstdint>
40#include <memory>
41#include <optional>
42#include <utility>
43#include <vector>
44
45namespace clang {
46
47class CFG;
48class Decl;
49class Expr;
50class ParentMap;
51class Stmt;
52
53namespace ento {
54
55class ExplodedGraph;
56
57//===----------------------------------------------------------------------===//
58// ExplodedGraph "implementation" classes. These classes are not typed to
59// contain a specific kind of state. Typed-specialized versions are defined
60// on top of these classes.
61//===----------------------------------------------------------------------===//
62
63// ExplodedNode is not constified all over the engine because we need to add
64// successors to it at any time after creating it.
65
66class ExplodedNode : public llvm::FoldingSetNode {
67 friend class BranchNodeBuilder;
68 friend class CoreEngine;
69 friend class ExplodedGraph;
70 friend class NodeBuilder;
71 friend class SwitchNodeBuilder;
72
73 /// Efficiently stores a list of ExplodedNodes, or an optional flag.
74 ///
75 /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing
76 /// for the case when there is only one node in the group. This is a fairly
77 /// common case in an ExplodedGraph, where most nodes have only one
78 /// predecessor and many have only one successor. It can also be used to
79 /// store a flag rather than a node list, which ExplodedNode uses to mark
80 /// whether a node is a sink. If the flag is set, the group is implicitly
81 /// empty and no nodes may be added.
82 class NodeGroup {
83 // Conceptually a discriminated union. If the low bit is set, the node is
84 // a sink. If the low bit is not set, the pointer refers to the storage
85 // for the nodes in the group.
86 // This is not a PointerIntPair in order to keep the storage type opaque.
87 uintptr_t P;
88
89 public:
90 NodeGroup(bool Flag = false) : P(Flag) {
91 assert(getFlag() == Flag);
92 }
93
94 ExplodedNode * const *begin() const;
95
96 ExplodedNode * const *end() const;
97
98 unsigned size() const;
99
100 bool empty() const { return P == 0 || getFlag() != 0; }
101
102 /// Adds a node to the list.
103 ///
104 /// The group must not have been created with its flag set.
105 void addNode(ExplodedNode *N, ExplodedGraph &G);
106
107 /// Replaces the single node in this group with a new node.
108 ///
109 /// Note that this should only be used when you know the group was not
110 /// created with its flag set, and that the group is empty or contains
111 /// only a single node.
112 void replaceNode(ExplodedNode *node);
113
114 /// Returns whether this group was created with its flag set.
115 bool getFlag() const {
116 return (P & 1);
117 }
118 };
119
120 /// Location - The program location (within a function body) associated
121 /// with this node.
122 const ProgramPoint Location;
123
124 /// State - The state associated with this node.
125 ProgramStateRef State;
126
127 /// Preds - The predecessors of this node.
128 NodeGroup Preds;
129
130 /// Succs - The successors of this node.
131 NodeGroup Succs;
132
133 int64_t Id;
134
135public:
137 int64_t Id, bool IsSink)
138 : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) {
139 assert(isSink() == IsSink);
140 }
141
142 /// getLocation - Returns the edge associated with the given node.
143 ProgramPoint getLocation() const { return Location; }
144
147 }
148
150 return getLocation().getStackFrame();
151 }
152
153 const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); }
154
155 CFG &getCFG() const { return *getLocationContext()->getCFG(); }
156
157 const CFGBlock *getCFGBlock() const;
158
159 const ParentMap &getParentMap() const {
161 }
162
163 template <typename T> T &getAnalysis() const {
164 return *getLocationContext()->getAnalysis<T>();
165 }
166
167 const ProgramStateRef &getState() const { return State; }
168
169 template <typename T> std::optional<T> getLocationAs() const & {
170 return Location.getAs<T>();
171 }
172
173 /// Get the value of an arbitrary expression at this node.
174 SVal getSVal(const Stmt *S) const {
175 return getState()->getSVal(S, getLocationContext());
176 }
177
178 static void Profile(llvm::FoldingSetNodeID &ID,
179 const ProgramPoint &Loc,
180 const ProgramStateRef &state,
181 bool IsSink) {
182 ID.Add(Loc);
183 ID.AddPointer(state.get());
184 ID.AddBoolean(IsSink);
185 }
186
187 void Profile(llvm::FoldingSetNodeID& ID) const {
188 // We avoid copy constructors by not using accessors.
189 Profile(ID, Location, State, isSink());
190 }
191
192 /// addPredeccessor - Adds a predecessor to the current node, and
193 /// in tandem add this node as a successor of the other node.
195
196 unsigned succ_size() const { return Succs.size(); }
197 unsigned pred_size() const { return Preds.size(); }
198 bool succ_empty() const { return Succs.empty(); }
199 bool pred_empty() const { return Preds.empty(); }
200
201 bool isSink() const { return Succs.getFlag(); }
202
203 bool hasSinglePred() const {
204 return (pred_size() == 1);
205 }
206
208 return pred_empty() ? nullptr : *(pred_begin());
209 }
210
211 const ExplodedNode *getFirstPred() const {
212 return const_cast<ExplodedNode*>(this)->getFirstPred();
213 }
214
216 return succ_empty() ? nullptr : *(succ_begin());
217 }
218
219 const ExplodedNode *getFirstSucc() const {
220 return const_cast<ExplodedNode*>(this)->getFirstSucc();
221 }
222
223 // Iterators over successor and predecessor vertices.
224 using succ_iterator = ExplodedNode * const *;
225 using succ_range = llvm::iterator_range<succ_iterator>;
226
227 using const_succ_iterator = const ExplodedNode * const *;
228 using const_succ_range = llvm::iterator_range<const_succ_iterator>;
229
230 using pred_iterator = ExplodedNode * const *;
231 using pred_range = llvm::iterator_range<pred_iterator>;
232
233 using const_pred_iterator = const ExplodedNode * const *;
234 using const_pred_range = llvm::iterator_range<const_pred_iterator>;
235
236 pred_iterator pred_begin() { return Preds.begin(); }
237 pred_iterator pred_end() { return Preds.end(); }
238 pred_range preds() { return {Preds.begin(), Preds.end()}; }
239
241 return const_cast<ExplodedNode*>(this)->pred_begin();
242 }
244 return const_cast<ExplodedNode*>(this)->pred_end();
245 }
246 const_pred_range preds() const { return {Preds.begin(), Preds.end()}; }
247
248 succ_iterator succ_begin() { return Succs.begin(); }
249 succ_iterator succ_end() { return Succs.end(); }
250 succ_range succs() { return {Succs.begin(), Succs.end()}; }
251
253 return const_cast<ExplodedNode*>(this)->succ_begin();
254 }
256 return const_cast<ExplodedNode*>(this)->succ_end();
257 }
258 const_succ_range succs() const { return {Succs.begin(), Succs.end()}; }
259
260 int64_t getID() const { return Id; }
261
262 /// The node is trivial if it has only one successor, only one predecessor,
263 /// it's predecessor has only one successor,
264 /// and its program state is the same as the program state of the previous
265 /// node.
266 /// Trivial nodes may be skipped while printing exploded graph.
267 bool isTrivial() const;
268
269 /// If the node's program point corresponds to a statement, retrieve that
270 /// statement. Useful for figuring out where to put a warning or a note.
271 /// If the statement belongs to a body-farmed definition,
272 /// retrieve the call site for that definition.
273 const Stmt *getStmtForDiagnostics() const;
274
275 /// Find the next statement that was executed on this node's execution path.
276 /// Useful for explaining control flow that follows the current node.
277 /// If the statement belongs to a body-farmed definition, retrieve the
278 /// call site for that definition.
279 const Stmt *getNextStmtForDiagnostics() const;
280
281 /// Find the statement that was executed immediately before this node.
282 /// Useful when the node corresponds to a CFG block entrance.
283 /// If the statement belongs to a body-farmed definition, retrieve the
284 /// call site for that definition.
285 const Stmt *getPreviousStmtForDiagnostics() const;
286
287 /// Find the statement that was executed at or immediately before this node.
288 /// Useful when any nearby statement will do.
289 /// If the statement belongs to a body-farmed definition, retrieve the
290 /// call site for that definition.
292
293private:
294 void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); }
295 void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); }
296};
297
299 llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>;
300
302protected:
303 friend class CoreEngine;
304
305 // Type definitions.
306 using NodeVector = std::vector<ExplodedNode *>;
307
308 /// The root of the simulation graph. Can be nullptr if the graph is empty or
309 /// if it was populated by `createUncachedNode()`.
310 ExplodedNode *Root = nullptr;
311
312 /// The nodes in the simulation graph which have been
313 /// specially marked as the endpoint of an abstract simulation path.
315
316 /// Nodes - The nodes in the graph.
317 llvm::FoldingSet<ExplodedNode> Nodes;
318
319 /// BVC - Allocator and context for allocating nodes and their predecessor
320 /// and successor groups.
322
323 /// NumNodes - The number of nodes in the graph.
324 int64_t NumNodes = 0;
325
326 /// A list of recently allocated nodes that can potentially be recycled.
328
329 /// A list of nodes that can be reused.
331
332 /// Determines how often nodes are reclaimed.
333 ///
334 /// If this is 0, nodes will never be reclaimed.
336
337 /// Counter to determine when to reclaim nodes.
339
340public:
343
344 /// Get the root node of the graph. This may return nullptr if the graph is
345 /// empty or under construction.
346 ExplodedNode *getRoot() const { return Root; }
347
348 /// Retrieve the node associated with a (Location, State) pair, where the
349 /// 'Location' is a ProgramPoint in the CFG. If no node for this pair exists,
350 /// it is created. IsNew is set to true if the node was freshly created.
352 bool IsSink = false,
353 bool* IsNew = nullptr);
354
355 /// Create a node for a (Location, State) pair, but don't store it for
356 /// deduplication later. This is useful when copying some nodes from an
357 /// already completed ExplodedGraph for further processing.
359 ProgramStateRef State,
360 int64_t Id,
361 bool IsSink = false);
362
363 /// Mark a node as the root of the graph. Calling this is an error if the
364 /// graph already has a root node.
366 assert(V && "Cannot designate nullptr as root!");
367 assert(!Root && "The graph already has a root, cannot designate another!");
368 Root = V;
369 }
370
371 /// addEndOfPath - Add an untyped node to the set of EOP nodes.
373 EndNodes.push_back(V);
374 return V;
375 }
376
377 unsigned num_eops() const { return EndNodes.size(); }
378
379 bool empty() const { return NumNodes == 0; }
380 unsigned size() const { return NumNodes; }
381
382 void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); }
383
384 // Iterators.
386 using AllNodesTy = llvm::FoldingSet<ExplodedNode>;
387 using eop_iterator = NodeVector::iterator;
388 using const_eop_iterator = NodeVector::const_iterator;
389 using node_iterator = AllNodesTy::iterator;
390 using const_node_iterator = AllNodesTy::const_iterator;
391
392 llvm::iterator_range<node_iterator> nodes() { return Nodes; }
393
394 llvm::iterator_range<const_node_iterator> nodes() const { return Nodes; }
395
396 eop_iterator eop_begin() { return EndNodes.begin(); }
397
398 eop_iterator eop_end() { return EndNodes.end(); }
399
400 const_eop_iterator eop_begin() const { return EndNodes.begin(); }
401
402 const_eop_iterator eop_end() const { return EndNodes.end(); }
403
404 llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); }
406
407 using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>;
408
409 /// Creates a trimmed version of the graph that only contains paths leading
410 /// to the given nodes.
411 ///
412 /// \param Nodes The nodes which must appear in the final graph. Presumably
413 /// these are end-of-path nodes (i.e. they have no successors).
414 /// \param[out] ForwardMap An optional map from nodes in this graph to nodes
415 /// in the returned graph.
416 /// \param[out] InverseMap An optional map from nodes in the returned graph to
417 /// nodes in this graph.
418 /// \returns The trimmed graph
419 std::unique_ptr<ExplodedGraph>
421 InterExplodedGraphMap *ForwardMap = nullptr,
422 InterExplodedGraphMap *InverseMap = nullptr) const;
423
424 /// Enable tracking of recently allocated nodes for potential reclamation
425 /// when calling reclaimRecentlyAllocatedNodes().
426 void enableNodeReclamation(unsigned Interval) {
428 }
429
430 /// Reclaim "uninteresting" nodes created since the last time this method
431 /// was called.
433
434 /// Returns true if nodes for the given expression kind are always
435 /// kept around.
436 static bool isInterestingLValueExpr(const Expr *Ex);
437
438private:
439 bool shouldCollect(const ExplodedNode *node);
440 void collectNode(ExplodedNode *node);
441};
442
443/// ExplodedNodeSet is a set of `ExplodedNode *` elements with the invariant
444/// that its elements cannot be nullpointers or sink nodes. Insertion of null
445/// or sink nodes is silently ignored (which is comfortable in many use cases).
446/// Note that `ExplodedNode *` is implicitly convertible to an
447/// `ExplodedNodeSet` containing 0 or 1 elements (where null pointers and sink
448/// nodes converted to the empty set).
449/// This type has set semantics (repeated insertions are ignored), but the
450/// iteration order is always the order of (first) insertion.
453 ImplTy Impl;
454
455public:
457
458 ExplodedNodeSet() = default;
459
460 using iterator = ImplTy::iterator;
461 using const_iterator = ImplTy::const_iterator;
462
463 unsigned size() const { return Impl.size(); }
464 bool empty() const { return Impl.empty(); }
465 bool erase(ExplodedNode *N) { return Impl.remove(N); }
466
467 void clear() { Impl.clear(); }
468
470 if (N && !N->isSink())
471 Impl.insert(N);
472 }
473
474 void insert(const ExplodedNodeSet &S) {
475 if (&S == this)
476 return;
477 if (empty())
478 Impl = S.Impl;
479 else
480 Impl.insert_range(S);
481 }
482
483 iterator begin() { return Impl.begin(); }
484 iterator end() { return Impl.end(); }
485
486 const_iterator begin() const { return Impl.begin(); }
487 const_iterator end() const { return Impl.end(); }
488};
489
490} // namespace ento
491
492} // namespace clang
493
494// GraphTraits
495
496namespace llvm {
497 template <> struct GraphTraits<clang::ento::ExplodedGraph *> {
501 using nodes_iterator = llvm::df_iterator<GraphTy>;
502
503 static NodeRef getEntryNode(const GraphTy G) { return G->getRoot(); }
504
506 return N->succ_size() == 1 && N->getFirstSucc()->isTrivial();
507 }
508
511 return child_begin(*N->succ_begin());
512 return N->succ_begin();
513 }
514
517 return child_end(N->getFirstSucc());
518 return N->succ_end();
519 }
520
522 return df_begin(G);
523 }
524
526 return df_end(G);
527 }
528 };
529} // namespace llvm
530
531#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H
#define V(N, I)
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Represents a single basic block in a source-level CFG.
Definition CFG.h:632
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1250
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:112
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const ParentMap & getParentMap() const
const StackFrameContext * getStackFrame() const
const LocationContext * getLocationContext() const
It represents a stack frame of the call stack (based on CallEvent).
Stmt - This represents one statement.
Definition Stmt.h:86
std::unique_ptr< ExplodedGraph > trim(ArrayRef< const NodeTy * > Nodes, InterExplodedGraphMap *ForwardMap=nullptr, InterExplodedGraphMap *InverseMap=nullptr) const
Creates a trimmed version of the graph that only contains paths leading to the given nodes.
BumpVectorContext & getNodeAllocator()
std::vector< ExplodedNode * > NodeVector
unsigned ReclaimCounter
Counter to determine when to reclaim nodes.
void reserve(unsigned NodeCount)
NodeVector ChangedNodes
A list of recently allocated nodes that can potentially be recycled.
int64_t NumNodes
NumNodes - The number of nodes in the graph.
void enableNodeReclamation(unsigned Interval)
Enable tracking of recently allocated nodes for potential reclamation when calling reclaimRecentlyAll...
unsigned ReclaimNodeInterval
Determines how often nodes are reclaimed.
NodeVector EndNodes
The nodes in the simulation graph which have been specially marked as the endpoint of an abstract sim...
NodeVector::iterator eop_iterator
llvm::iterator_range< const_node_iterator > nodes() const
void reclaimRecentlyAllocatedNodes()
Reclaim "uninteresting" nodes created since the last time this method was called.
static bool isInterestingLValueExpr(const Expr *Ex)
Returns true if nodes for the given expression kind are always kept around.
AllNodesTy::const_iterator const_node_iterator
llvm::BumpPtrAllocator & getAllocator()
AllNodesTy::iterator node_iterator
llvm::FoldingSet< ExplodedNode > AllNodesTy
ExplodedNode * getNode(const ProgramPoint &L, ProgramStateRef State, bool IsSink=false, bool *IsNew=nullptr)
Retrieve the node associated with a (Location, State) pair, where the 'Location' is a ProgramPoint in...
const_eop_iterator eop_begin() const
NodeVector::const_iterator const_eop_iterator
BumpVectorContext BVC
BVC - Allocator and context for allocating nodes and their predecessor and successor groups.
llvm::iterator_range< node_iterator > nodes()
ExplodedNode * getRoot() const
Get the root node of the graph.
void designateAsRoot(ExplodedNode *V)
Mark a node as the root of the graph.
ExplodedNode * createUncachedNode(const ProgramPoint &L, ProgramStateRef State, int64_t Id, bool IsSink=false)
Create a node for a (Location, State) pair, but don't store it for deduplication later.
llvm::FoldingSet< ExplodedNode > Nodes
Nodes - The nodes in the graph.
llvm::DenseMap< const ExplodedNode *, ExplodedNode * > NodeMap
ExplodedNode * Root
The root of the simulation graph.
ExplodedNode * addEndOfPath(ExplodedNode *V)
addEndOfPath - Add an untyped node to the set of EOP nodes.
NodeVector FreeNodes
A list of nodes that can be reused.
const_eop_iterator eop_end() const
void insert(ExplodedNode *N)
bool erase(ExplodedNode *N)
ExplodedNodeSet(ExplodedNode *N)
ImplTy::const_iterator const_iterator
void insert(const ExplodedNodeSet &S)
const_iterator end() const
const_iterator begin() const
const CFGBlock * getCFGBlock() const
llvm::iterator_range< pred_iterator > pred_range
const ProgramStateRef & getState() const
llvm::iterator_range< const_succ_iterator > const_succ_range
const_pred_iterator pred_begin() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const_succ_iterator succ_begin() const
llvm::iterator_range< succ_iterator > succ_range
bool isTrivial() const
The node is trivial if it has only one successor, only one predecessor, it's predecessor has only one...
const Stmt * getPreviousStmtForDiagnostics() const
Find the statement that was executed immediately before this node.
void Profile(llvm::FoldingSetNodeID &ID) 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 ...
llvm::iterator_range< const_pred_iterator > const_pred_range
ExplodedNode * getFirstSucc()
unsigned pred_size() const
static void Profile(llvm::FoldingSetNodeID &ID, const ProgramPoint &Loc, const ProgramStateRef &state, bool IsSink)
const ExplodedNode * getFirstSucc() const
ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, int64_t Id, bool IsSink)
const_succ_range succs() const
const Stmt * getNextStmtForDiagnostics() const
Find the next statement that was executed on this node's execution path.
const StackFrameContext * getStackFrame() const
ExplodedNode *const * succ_iterator
const ExplodedNode * getFirstPred() const
const_pred_range preds() const
const ParentMap & getParentMap() const
SVal getSVal(const Stmt *S) const
Get the value of an arbitrary expression at this node.
const LocationContext * getLocationContext() const
std::optional< T > getLocationAs() const &
const Stmt * getCurrentOrPreviousStmtForDiagnostics() const
Find the statement that was executed at or immediately before this node.
const ExplodedNode *const * const_pred_iterator
ExplodedNode * getFirstPred()
const_succ_iterator succ_end() const
unsigned succ_size() const
ExplodedNode *const * pred_iterator
const ExplodedNode *const * const_succ_iterator
const Decl & getCodeDecl() const
const_pred_iterator pred_end() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:56
llvm::DenseMap< const ExplodedNode *, const ExplodedNode * > InterExplodedGraphMap
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
Definition Facts.h:89
The JSON file list parser is used to communicate input to InstallAPI.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
long int64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
__UINTPTR_TYPE__ uintptr_t
An unsigned integer type with the property that any valid pointer to void can be converted to this ty...
static nodes_iterator nodes_end(const GraphTy G)
static nodes_iterator nodes_begin(const GraphTy G)
static ChildIteratorType child_end(NodeRef N)
clang::ento::ExplodedNode::succ_iterator ChildIteratorType
static NodeRef getEntryNode(const GraphTy G)
static ChildIteratorType child_begin(NodeRef N)