clang 24.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 CoreEngine;
68 friend class ExplodedGraph;
69 friend class NodeBuilder;
70
71 /// Efficiently stores a list of ExplodedNodes, or an optional flag.
72 ///
73 /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing
74 /// for the case when there is only one node in the group. This is a fairly
75 /// common case in an ExplodedGraph, where most nodes have only one
76 /// predecessor and many have only one successor. It can also be used to
77 /// store a flag rather than a node list, which ExplodedNode uses to mark
78 /// whether a node is a sink. If the flag is set, the group is implicitly
79 /// empty and no nodes may be added.
80 class NodeGroup {
81 // Conceptually a discriminated union. If the low bit is set, the node is
82 // a sink. If the low bit is not set, the pointer refers to the storage
83 // for the nodes in the group.
84 // This is not a PointerIntPair in order to keep the storage type opaque.
85 uintptr_t P;
86
87 public:
88 NodeGroup(bool Flag = false) : P(Flag) {
89 assert(getFlag() == Flag);
90 }
91
92 ExplodedNode * const *begin() const;
93
94 ExplodedNode * const *end() const;
95
96 unsigned size() const;
97
98 bool empty() const { return P == 0 || getFlag() != 0; }
99
100 /// Adds a node to the list.
101 ///
102 /// The group must not have been created with its flag set.
103 void addNode(ExplodedNode *N, ExplodedGraph &G);
104
105 /// Replaces the single node in this group with a new node.
106 ///
107 /// Note that this should only be used when you know the group was not
108 /// created with its flag set, and that the group is empty or contains
109 /// only a single node.
110 void replaceNode(ExplodedNode *node);
111
112 /// Returns whether this group was created with its flag set.
113 bool getFlag() const {
114 return (P & 1);
115 }
116 };
117
118 /// Location - The program location (within a function body) associated
119 /// with this node.
120 const ProgramPoint Location;
121
122 /// State - The state associated with this node.
123 ProgramStateRef State;
124
125 /// Preds - The predecessors of this node.
126 NodeGroup Preds;
127
128 /// Succs - The successors of this node.
129 NodeGroup Succs;
130
131 int64_t Id;
132
133public:
135 int64_t Id, bool IsSink)
136 : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) {
137 assert(isSink() == IsSink);
138 }
139
140 /// getLocation - Returns the edge associated with the given node.
141 ProgramPoint getLocation() const { return Location; }
142
143 const StackFrame *getStackFrame() const {
144 return getLocation().getStackFrame();
145 }
146
147 /// Iterates over the current stack frame and all of its ancestors.
148 llvm::iterator_range<StackFrame::parent_iterator> stackframes() const {
150 }
151
152 const Decl &getCodeDecl() const { return *getStackFrame()->getDecl(); }
153
154 CFG &getCFG() const { return *getStackFrame()->getCFG(); }
155
156 const CFGBlock *getCFGBlock() const;
157
158 const ParentMap &getParentMap() const {
159 return getStackFrame()->getParentMap();
160 }
161
162 template <typename T> T &getAnalysis() const {
163 return *getStackFrame()->getAnalysis<T>();
164 }
165
166 const ProgramStateRef &getState() const { return State; }
167
168 template <typename T> std::optional<T> getLocationAs() const & {
169 return Location.getAs<T>();
170 }
171
172 /// Get the value of an arbitrary expression at this node.
173 SVal getSVal(const Expr *E) const {
174 return getState()->getSVal(E, getStackFrame());
175 }
176
177 static void Profile(llvm::FoldingSetNodeID &ID,
178 const ProgramPoint &Loc,
179 const ProgramStateRef &state,
180 bool IsSink) {
181 ID.Add(Loc);
182 ID.AddPointer(state.get());
183 ID.AddBoolean(IsSink);
184 }
185
186 void Profile(llvm::FoldingSetNodeID& ID) const {
187 // We avoid copy constructors by not using accessors.
188 Profile(ID, Location, State, isSink());
189 }
190
191 /// addPredeccessor - Adds a predecessor to the current node, and
192 /// in tandem add this node as a successor of the other node.
194
195 unsigned succ_size() const { return Succs.size(); }
196 unsigned pred_size() const { return Preds.size(); }
197 bool succ_empty() const { return Succs.empty(); }
198 bool pred_empty() const { return Preds.empty(); }
199
200 bool isSink() const { return Succs.getFlag(); }
201
202 bool hasSinglePred() const {
203 return (pred_size() == 1);
204 }
205
207 return pred_empty() ? nullptr : *(pred_begin());
208 }
209
210 const ExplodedNode *getFirstPred() const {
211 return const_cast<ExplodedNode*>(this)->getFirstPred();
212 }
213
215 return succ_empty() ? nullptr : *(succ_begin());
216 }
217
218 const ExplodedNode *getFirstSucc() const {
219 return const_cast<ExplodedNode*>(this)->getFirstSucc();
220 }
221
222 // Iterators over successor and predecessor vertices.
223 using succ_iterator = ExplodedNode * const *;
224 using succ_range = llvm::iterator_range<succ_iterator>;
225
226 using const_succ_iterator = const ExplodedNode * const *;
227 using const_succ_range = llvm::iterator_range<const_succ_iterator>;
228
229 using pred_iterator = ExplodedNode * const *;
230 using pred_range = llvm::iterator_range<pred_iterator>;
231
232 using const_pred_iterator = const ExplodedNode * const *;
233 using const_pred_range = llvm::iterator_range<const_pred_iterator>;
234
235 pred_iterator pred_begin() { return Preds.begin(); }
236 pred_iterator pred_end() { return Preds.end(); }
237 pred_range preds() { return {Preds.begin(), Preds.end()}; }
238
240 return const_cast<ExplodedNode*>(this)->pred_begin();
241 }
243 return const_cast<ExplodedNode*>(this)->pred_end();
244 }
245 const_pred_range preds() const { return {Preds.begin(), Preds.end()}; }
246
247 succ_iterator succ_begin() { return Succs.begin(); }
248 succ_iterator succ_end() { return Succs.end(); }
249 succ_range succs() { return {Succs.begin(), Succs.end()}; }
250
252 return const_cast<ExplodedNode*>(this)->succ_begin();
253 }
255 return const_cast<ExplodedNode*>(this)->succ_end();
256 }
257 const_succ_range succs() const { return {Succs.begin(), Succs.end()}; }
258
259 int64_t getID() const { return Id; }
260
261 /// The node is trivial if it has only one successor, only one predecessor,
262 /// it's predecessor has only one successor,
263 /// and its program state is the same as the program state of the previous
264 /// node.
265 /// Trivial nodes may be skipped while printing exploded graph.
266 bool isTrivial() const;
267
268 /// If the node's program point corresponds to a statement, retrieve that
269 /// statement. Useful for figuring out where to put a warning or a note.
270 /// If the statement belongs to a body-farmed definition,
271 /// retrieve the call site for that definition.
272 const Stmt *getStmtForDiagnostics() const;
273
274 /// Find the next statement that was executed on this node's execution path.
275 /// Useful for explaining control flow that follows the current node.
276 /// If the statement belongs to a body-farmed definition, retrieve the
277 /// call site for that definition.
278 const Stmt *getNextStmtForDiagnostics() const;
279
280 /// Find the statement that was executed immediately before this node.
281 /// Useful when the node corresponds to a CFG block entrance.
282 /// If the statement belongs to a body-farmed definition, retrieve the
283 /// call site for that definition.
284 const Stmt *getPreviousStmtForDiagnostics() const;
285
286 /// Find the statement that was executed at or immediately before this node.
287 /// Useful when any nearby statement will do.
288 /// If the statement belongs to a body-farmed definition, retrieve the
289 /// call site for that definition.
291
292private:
293 void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); }
294 void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); }
295};
296
298 llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>;
299
301protected:
302 friend class CoreEngine;
303
304 // Type definitions.
305 using NodeVector = std::vector<ExplodedNode *>;
306
307 /// The root of the simulation graph. Can be nullptr if the graph is empty or
308 /// if it was populated by `createUncachedNode()`.
309 ExplodedNode *Root = nullptr;
310
311 /// The nodes in the simulation graph which have been
312 /// specially marked as the endpoint of an abstract simulation path.
314
315 /// Nodes - The nodes in the graph.
316 llvm::FoldingSet<ExplodedNode> Nodes;
317
318 /// BVC - Allocator and context for allocating nodes and their predecessor
319 /// and successor groups.
321
322 /// NumNodes - The number of nodes in the graph.
323 int64_t NumNodes = 0;
324
325 /// A list of recently allocated nodes that can potentially be recycled.
327
328 /// A list of nodes that can be reused.
330
331 /// Determines how often nodes are reclaimed.
332 ///
333 /// If this is 0, nodes will never be reclaimed.
335
336 /// Counter to determine when to reclaim nodes.
338
339public:
342
343 /// Get the root node of the graph. This may return nullptr if the graph is
344 /// empty or under construction.
345 ExplodedNode *getRoot() const { return Root; }
346
347 /// Retrieve the node associated with a (Location, State) pair, where the
348 /// 'Location' is a ProgramPoint in the CFG. If no node for this pair exists,
349 /// it is created. IsNew is set to true if the node was freshly created.
351 bool IsSink = false,
352 bool* IsNew = nullptr);
353
354 /// Create a node for a (Location, State) pair, but don't store it for
355 /// deduplication later. This is useful when copying some nodes from an
356 /// already completed ExplodedGraph for further processing.
358 ProgramStateRef State,
359 int64_t Id,
360 bool IsSink = false);
361
362 /// Mark a node as the root of the graph. Calling this is an error if the
363 /// graph already has a root node.
365 assert(V && "Cannot designate nullptr as root!");
366 assert(!Root && "The graph already has a root, cannot designate another!");
367 Root = V;
368 }
369
370 /// addEndOfPath - Add an untyped node to the set of EOP nodes.
372 EndNodes.push_back(V);
373 return V;
374 }
375
376 unsigned num_eops() const { return EndNodes.size(); }
377
378 bool empty() const { return NumNodes == 0; }
379 unsigned size() const { return NumNodes; }
380
381 void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); }
382
383 // Iterators.
385 using AllNodesTy = llvm::FoldingSet<ExplodedNode>;
386 using eop_iterator = NodeVector::iterator;
387 using const_eop_iterator = NodeVector::const_iterator;
388 using node_iterator = AllNodesTy::iterator;
389 using const_node_iterator = AllNodesTy::const_iterator;
390
391 llvm::iterator_range<node_iterator> nodes() { return Nodes; }
392
393 llvm::iterator_range<const_node_iterator> nodes() const { return Nodes; }
394
395 eop_iterator eop_begin() { return EndNodes.begin(); }
396
397 eop_iterator eop_end() { return EndNodes.end(); }
398
399 const_eop_iterator eop_begin() const { return EndNodes.begin(); }
400
401 const_eop_iterator eop_end() const { return EndNodes.end(); }
402
403 llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); }
405
406 using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>;
407
408 /// Creates a trimmed version of the graph that only contains paths leading
409 /// to the given nodes.
410 ///
411 /// \param Nodes The nodes which must appear in the final graph. Presumably
412 /// these are end-of-path nodes (i.e. they have no successors).
413 /// \param[out] ForwardMap An optional map from nodes in this graph to nodes
414 /// in the returned graph.
415 /// \param[out] InverseMap An optional map from nodes in the returned graph to
416 /// nodes in this graph.
417 /// \returns The trimmed graph
418 std::unique_ptr<ExplodedGraph>
420 InterExplodedGraphMap *ForwardMap = nullptr,
421 InterExplodedGraphMap *InverseMap = nullptr) const;
422
423 /// Enable tracking of recently allocated nodes for potential reclamation
424 /// when calling reclaimRecentlyAllocatedNodes().
425 void enableNodeReclamation(unsigned Interval) {
427 }
428
429 /// Reclaim "uninteresting" nodes created since the last time this method
430 /// was called.
432
433 /// Returns true if nodes for the given expression kind are always
434 /// kept around.
435 static bool isInterestingLValueExpr(const Expr *Ex);
436
437private:
438 bool shouldCollect(const ExplodedNode *node);
439 void collectNode(ExplodedNode *node);
440};
441
442/// ExplodedNodeSet is a set of `ExplodedNode *` elements with the invariant
443/// that its elements cannot be nullpointers or sink nodes. Insertion of null
444/// or sink nodes is silently ignored (which is comfortable in many use cases).
445/// Note that `ExplodedNode *` is implicitly convertible to an
446/// `ExplodedNodeSet` containing 0 or 1 elements (where null pointers and sink
447/// nodes converted to the empty set).
448/// This type has set semantics (repeated insertions are ignored), but the
449/// iteration order is always the order of (first) insertion.
452 ImplTy Impl;
453
454public:
456
457 ExplodedNodeSet() = default;
458
459 using iterator = ImplTy::iterator;
460 using const_iterator = ImplTy::const_iterator;
461
462 unsigned size() const { return Impl.size(); }
463 bool empty() const { return Impl.empty(); }
464 bool erase(ExplodedNode *N) { return Impl.remove(N); }
465
466 void clear() { Impl.clear(); }
467
469 if (N && !N->isSink())
470 Impl.insert(N);
471 }
472
473 void insert(const ExplodedNodeSet &S) {
474 if (&S == this)
475 return;
476 if (empty())
477 Impl = S.Impl;
478 else
479 Impl.insert_range(S);
480 }
481
482 iterator begin() { return Impl.begin(); }
483 iterator end() { return Impl.end(); }
484
485 const_iterator begin() const { return Impl.begin(); }
486 const_iterator end() const { return Impl.end(); }
487};
488
489} // namespace ento
490
491} // namespace clang
492
493// GraphTraits
494
495namespace llvm {
496 template <> struct GraphTraits<clang::ento::ExplodedGraph *> {
500 using nodes_iterator = llvm::df_iterator<GraphTy>;
501
502 static NodeRef getEntryNode(const GraphTy G) { return G->getRoot(); }
503
505 return N->succ_size() == 1 && N->getFirstSucc()->isTrivial();
506 }
507
510 return child_begin(*N->succ_begin());
511 return N->succ_begin();
512 }
513
516 return child_end(N->getFirstSucc());
517 return N->succ_end();
518 }
519
521 return df_begin(G);
522 }
523
525 return df_end(G);
526 }
527 };
528} // namespace llvm
529
530#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:652
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1271
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:112
const StackFrame * getStackFrame() const
It represents a stack frame of the call stack.
const ParentMap & getParentMap() const
const Decl * getDecl() const
llvm::iterator_range< parent_iterator > parentsIncludingSelf() const
Iterates over this frame followed by all of its ancestors.
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
SVal getSVal(const Expr *E) const
Get the value of an arbitrary expression at this node.
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.
ExplodedNode *const * succ_iterator
const ExplodedNode * getFirstPred() const
const_pred_range preds() const
const ParentMap & getParentMap() const
std::optional< T > getLocationAs() const &
const Stmt * getCurrentOrPreviousStmtForDiagnostics() const
Find the statement that was executed at or immediately before this node.
llvm::iterator_range< StackFrame::parent_iterator > stackframes() const
Iterates over the current stack frame and all of its ancestors.
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
const StackFrame * getStackFrame() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:57
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:95
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',...
const FunctionProtoType * T
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)