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