ExplodedGraph.h revision 276479
1218729Shselasky//=-- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -*- C++ -*-------==// 2218729Shselasky// 3218729Shselasky// The LLVM Compiler Infrastructure 4218729Shselasky// 5218729Shselasky// This file is distributed under the University of Illinois Open Source 6218729Shselasky// License. See LICENSE.TXT for details. 7218729Shselasky// 8218729Shselasky//===----------------------------------------------------------------------===// 9218729Shselasky// 10218729Shselasky// This file defines the template classes ExplodedNode and ExplodedGraph, 11218729Shselasky// which represent a path-sensitive, intra-procedural "exploded graph." 12218729Shselasky// See "Precise interprocedural dataflow analysis via graph reachability" 13218729Shselasky// by Reps, Horwitz, and Sagiv 14218729Shselasky// (http://portal.acm.org/citation.cfm?id=199462) for the definition of an 15218729Shselasky// exploded graph. 16218729Shselasky// 17218729Shselasky//===----------------------------------------------------------------------===// 18218729Shselasky 19218729Shselasky#ifndef LLVM_CLANG_GR_EXPLODEDGRAPH 20218729Shselasky#define LLVM_CLANG_GR_EXPLODEDGRAPH 21218729Shselasky 22218729Shselasky#include "clang/AST/Decl.h" 23218729Shselasky#include "clang/Analysis/AnalysisContext.h" 24218729Shselasky#include "clang/Analysis/ProgramPoint.h" 25218729Shselasky#include "clang/Analysis/Support/BumpVector.h" 26218729Shselasky#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 27218729Shselasky#include "llvm/ADT/DepthFirstIterator.h" 28218729Shselasky#include "llvm/ADT/FoldingSet.h" 29218729Shselasky#include "llvm/ADT/GraphTraits.h" 30218729Shselasky#include "llvm/ADT/SmallPtrSet.h" 31218729Shselasky#include "llvm/ADT/SmallVector.h" 32218729Shselasky#include "llvm/Support/Allocator.h" 33218729Shselasky#include "llvm/Support/Casting.h" 34218729Shselasky#include <memory> 35218729Shselasky#include <vector> 36218729Shselasky 37218729Shselaskynamespace clang { 38218729Shselasky 39218729Shselaskyclass CFG; 40218729Shselasky 41218729Shselaskynamespace ento { 42218729Shselasky 43218729Shselaskyclass ExplodedGraph; 44218729Shselasky 45218729Shselasky//===----------------------------------------------------------------------===// 46218729Shselasky// ExplodedGraph "implementation" classes. These classes are not typed to 47218729Shselasky// contain a specific kind of state. Typed-specialized versions are defined 48218729Shselasky// on top of these classes. 49218729Shselasky//===----------------------------------------------------------------------===// 50218729Shselasky 51218729Shselasky// ExplodedNode is not constified all over the engine because we need to add 52218729Shselasky// successors to it at any time after creating it. 53218729Shselasky 54218729Shselaskyclass ExplodedNode : public llvm::FoldingSetNode { 55218729Shselasky friend class ExplodedGraph; 56218729Shselasky friend class CoreEngine; 57218729Shselasky friend class NodeBuilder; 58218729Shselasky friend class BranchNodeBuilder; 59218729Shselasky friend class IndirectGotoNodeBuilder; 60218729Shselasky friend class SwitchNodeBuilder; 61218729Shselasky friend class EndOfFunctionNodeBuilder; 62218729Shselasky 63218729Shselasky /// Efficiently stores a list of ExplodedNodes, or an optional flag. 64218729Shselasky /// 65218729Shselasky /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing 66218729Shselasky /// for the case when there is only one node in the group. This is a fairly 67218729Shselasky /// common case in an ExplodedGraph, where most nodes have only one 68218729Shselasky /// predecessor and many have only one successor. It can also be used to 69218729Shselasky /// store a flag rather than a node list, which ExplodedNode uses to mark 70218729Shselasky /// whether a node is a sink. If the flag is set, the group is implicitly 71218729Shselasky /// empty and no nodes may be added. 72218729Shselasky class NodeGroup { 73218729Shselasky // Conceptually a discriminated union. If the low bit is set, the node is 74218729Shselasky // a sink. If the low bit is not set, the pointer refers to the storage 75218729Shselasky // for the nodes in the group. 76218729Shselasky // This is not a PointerIntPair in order to keep the storage type opaque. 77218729Shselasky uintptr_t P; 78218729Shselasky 79218729Shselasky public: 80218729Shselasky NodeGroup(bool Flag = false) : P(Flag) { 81218729Shselasky assert(getFlag() == Flag); 82218729Shselasky } 83218729Shselasky 84218729Shselasky ExplodedNode * const *begin() const; 85232257Skevlo 86218729Shselasky ExplodedNode * const *end() const; 87218729Shselasky 88218729Shselasky unsigned size() const; 89218729Shselasky 90218729Shselasky bool empty() const { return P == 0 || getFlag() != 0; } 91218729Shselasky 92218729Shselasky /// Adds a node to the list. 93218729Shselasky /// 94218729Shselasky /// The group must not have been created with its flag set. 95218729Shselasky void addNode(ExplodedNode *N, ExplodedGraph &G); 96218729Shselasky 97218729Shselasky /// Replaces the single node in this group with a new node. 98218729Shselasky /// 99218729Shselasky /// Note that this should only be used when you know the group was not 100218729Shselasky /// created with its flag set, and that the group is empty or contains 101218729Shselasky /// only a single node. 102218729Shselasky void replaceNode(ExplodedNode *node); 103218729Shselasky 104218729Shselasky /// Returns whether this group was created with its flag set. 105218729Shselasky bool getFlag() const { 106218729Shselasky return (P & 1); 107218729Shselasky } 108218729Shselasky }; 109218729Shselasky 110218729Shselasky /// Location - The program location (within a function body) associated 111218729Shselasky /// with this node. 112218729Shselasky const ProgramPoint Location; 113218729Shselasky 114218729Shselasky /// State - The state associated with this node. 115218729Shselasky ProgramStateRef State; 116218729Shselasky 117218729Shselasky /// Preds - The predecessors of this node. 118218729Shselasky NodeGroup Preds; 119218729Shselasky 120218729Shselasky /// Succs - The successors of this node. 121218729Shselasky NodeGroup Succs; 122218729Shselasky 123218729Shselaskypublic: 124218729Shselasky 125218729Shselasky explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, 126218729Shselasky bool IsSink) 127218729Shselasky : Location(loc), State(state), Succs(IsSink) { 128218729Shselasky assert(isSink() == IsSink); 129218729Shselasky } 130218729Shselasky 131218729Shselasky ~ExplodedNode() {} 132218729Shselasky 133218729Shselasky /// getLocation - Returns the edge associated with the given node. 134218729Shselasky ProgramPoint getLocation() const { return Location; } 135227309Sed 136218729Shselasky const LocationContext *getLocationContext() const { 137218729Shselasky return getLocation().getLocationContext(); 138218729Shselasky } 139218729Shselasky 140218729Shselasky const StackFrameContext *getStackFrame() const { 141218729Shselasky return getLocationContext()->getCurrentStackFrame(); 142218729Shselasky } 143218729Shselasky 144218729Shselasky const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } 145218729Shselasky 146218729Shselasky CFG &getCFG() const { return *getLocationContext()->getCFG(); } 147218729Shselasky 148218729Shselasky ParentMap &getParentMap() const {return getLocationContext()->getParentMap();} 149223486Shselasky 150218729Shselasky template <typename T> 151218729Shselasky T &getAnalysis() const { 152232257Skevlo return *getLocationContext()->getAnalysis<T>(); 153218729Shselasky } 154218729Shselasky 155218729Shselasky const ProgramStateRef &getState() const { return State; } 156218729Shselasky 157218729Shselasky template <typename T> 158218729Shselasky Optional<T> getLocationAs() const LLVM_LVALUE_FUNCTION { 159218729Shselasky return Location.getAs<T>(); 160218729Shselasky } 161218729Shselasky 162218729Shselasky static void Profile(llvm::FoldingSetNodeID &ID, 163218729Shselasky const ProgramPoint &Loc, 164218729Shselasky const ProgramStateRef &state, 165218729Shselasky bool IsSink) { 166218729Shselasky ID.Add(Loc); 167218729Shselasky ID.AddPointer(state.get()); 168218729Shselasky ID.AddBoolean(IsSink); 169218729Shselasky } 170218729Shselasky 171218729Shselasky void Profile(llvm::FoldingSetNodeID& ID) const { 172218729Shselasky // We avoid copy constructors by not using accessors. 173218729Shselasky Profile(ID, Location, State, isSink()); 174218729Shselasky } 175218729Shselasky 176218729Shselasky /// addPredeccessor - Adds a predecessor to the current node, and 177218729Shselasky /// in tandem add this node as a successor of the other node. 178218729Shselasky void addPredecessor(ExplodedNode *V, ExplodedGraph &G); 179218729Shselasky 180218729Shselasky unsigned succ_size() const { return Succs.size(); } 181218729Shselasky unsigned pred_size() const { return Preds.size(); } 182218729Shselasky bool succ_empty() const { return Succs.empty(); } 183218729Shselasky bool pred_empty() const { return Preds.empty(); } 184218729Shselasky 185218729Shselasky bool isSink() const { return Succs.getFlag(); } 186218729Shselasky 187218729Shselasky bool hasSinglePred() const { 188218729Shselasky return (pred_size() == 1); 189218729Shselasky } 190218729Shselasky 191218729Shselasky ExplodedNode *getFirstPred() { 192218729Shselasky return pred_empty() ? nullptr : *(pred_begin()); 193218729Shselasky } 194218729Shselasky 195218729Shselasky const ExplodedNode *getFirstPred() const { 196218729Shselasky return const_cast<ExplodedNode*>(this)->getFirstPred(); 197218729Shselasky } 198218729Shselasky 199218729Shselasky const ExplodedNode *getFirstSucc() const { 200218729Shselasky return succ_empty() ? nullptr : *(succ_begin()); 201218729Shselasky } 202218729Shselasky 203218729Shselasky // Iterators over successor and predecessor vertices. 204218729Shselasky typedef ExplodedNode* const * succ_iterator; 205218729Shselasky typedef const ExplodedNode* const * const_succ_iterator; 206218729Shselasky typedef ExplodedNode* const * pred_iterator; 207218729Shselasky typedef const ExplodedNode* const * const_pred_iterator; 208218729Shselasky 209218729Shselasky pred_iterator pred_begin() { return Preds.begin(); } 210218729Shselasky pred_iterator pred_end() { return Preds.end(); } 211218729Shselasky 212218729Shselasky const_pred_iterator pred_begin() const { 213218729Shselasky return const_cast<ExplodedNode*>(this)->pred_begin(); 214218729Shselasky } 215218729Shselasky const_pred_iterator pred_end() const { 216218729Shselasky return const_cast<ExplodedNode*>(this)->pred_end(); 217218729Shselasky } 218218729Shselasky 219218729Shselasky succ_iterator succ_begin() { return Succs.begin(); } 220218729Shselasky succ_iterator succ_end() { return Succs.end(); } 221218729Shselasky 222218729Shselasky const_succ_iterator succ_begin() const { 223218729Shselasky return const_cast<ExplodedNode*>(this)->succ_begin(); 224218729Shselasky } 225218729Shselasky const_succ_iterator succ_end() const { 226218729Shselasky return const_cast<ExplodedNode*>(this)->succ_end(); 227218729Shselasky } 228218729Shselasky 229227843Smarius // For debugging. 230218729Shselasky 231218729Shselaskypublic: 232218729Shselasky 233218729Shselasky class Auditor { 234218729Shselasky public: 235218729Shselasky virtual ~Auditor(); 236218729Shselasky virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst) = 0; 237218729Shselasky }; 238218729Shselasky 239218729Shselasky static void SetAuditor(Auditor* A); 240218729Shselasky 241218729Shselaskyprivate: 242218729Shselasky void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } 243218729Shselasky void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } 244218729Shselasky}; 245218729Shselasky 246218729Shselaskytypedef llvm::DenseMap<const ExplodedNode *, const ExplodedNode *> 247218729Shselasky InterExplodedGraphMap; 248218729Shselasky 249218729Shselaskyclass ExplodedGraph { 250218729Shselaskyprotected: 251218729Shselasky friend class CoreEngine; 252218729Shselasky 253218729Shselasky // Type definitions. 254218729Shselasky typedef std::vector<ExplodedNode *> NodeVector; 255218729Shselasky 256218729Shselasky /// The roots of the simulation graph. Usually there will be only 257218729Shselasky /// one, but clients are free to establish multiple subgraphs within a single 258218729Shselasky /// SimulGraph. Moreover, these subgraphs can often merge when paths from 259218729Shselasky /// different roots reach the same state at the same program location. 260218729Shselasky NodeVector Roots; 261218729Shselasky 262218729Shselasky /// The nodes in the simulation graph which have been 263218729Shselasky /// specially marked as the endpoint of an abstract simulation path. 264218729Shselasky NodeVector EndNodes; 265218729Shselasky 266218729Shselasky /// Nodes - The nodes in the graph. 267218729Shselasky llvm::FoldingSet<ExplodedNode> Nodes; 268218729Shselasky 269218729Shselasky /// BVC - Allocator and context for allocating nodes and their predecessor 270218729Shselasky /// and successor groups. 271218729Shselasky BumpVectorContext BVC; 272218729Shselasky 273218729Shselasky /// NumNodes - The number of nodes in the graph. 274218729Shselasky unsigned NumNodes; 275218729Shselasky 276218729Shselasky /// A list of recently allocated nodes that can potentially be recycled. 277218729Shselasky NodeVector ChangedNodes; 278218729Shselasky 279218729Shselasky /// A list of nodes that can be reused. 280218729Shselasky NodeVector FreeNodes; 281218729Shselasky 282218729Shselasky /// Determines how often nodes are reclaimed. 283218729Shselasky /// 284218729Shselasky /// If this is 0, nodes will never be reclaimed. 285218729Shselasky unsigned ReclaimNodeInterval; 286218729Shselasky 287218729Shselasky /// Counter to determine when to reclaim nodes. 288218729Shselasky unsigned ReclaimCounter; 289218729Shselasky 290218729Shselaskypublic: 291218729Shselasky 292218729Shselasky /// \brief Retrieve the node associated with a (Location,State) pair, 293218729Shselasky /// where the 'Location' is a ProgramPoint in the CFG. If no node for 294218729Shselasky /// this pair exists, it is created. IsNew is set to true if 295218729Shselasky /// the node was freshly created. 296218729Shselasky ExplodedNode *getNode(const ProgramPoint &L, ProgramStateRef State, 297218729Shselasky bool IsSink = false, 298218729Shselasky bool* IsNew = nullptr); 299218729Shselasky 300218729Shselasky ExplodedGraph* MakeEmptyGraph() const { 301218729Shselasky return new ExplodedGraph(); 302218729Shselasky } 303218729Shselasky 304218729Shselasky /// addRoot - Add an untyped node to the set of roots. 305218729Shselasky ExplodedNode *addRoot(ExplodedNode *V) { 306218729Shselasky Roots.push_back(V); 307218729Shselasky return V; 308218729Shselasky } 309218729Shselasky 310218729Shselasky /// addEndOfPath - Add an untyped node to the set of EOP nodes. 311218729Shselasky ExplodedNode *addEndOfPath(ExplodedNode *V) { 312218729Shselasky EndNodes.push_back(V); 313218729Shselasky return V; 314218729Shselasky } 315218729Shselasky 316218729Shselasky ExplodedGraph(); 317218729Shselasky 318218729Shselasky ~ExplodedGraph(); 319218729Shselasky 320218729Shselasky unsigned num_roots() const { return Roots.size(); } 321218729Shselasky unsigned num_eops() const { return EndNodes.size(); } 322218729Shselasky 323218729Shselasky bool empty() const { return NumNodes == 0; } 324218729Shselasky unsigned size() const { return NumNodes; } 325218729Shselasky 326218729Shselasky // Iterators. 327218729Shselasky typedef ExplodedNode NodeTy; 328218729Shselasky typedef llvm::FoldingSet<ExplodedNode> AllNodesTy; 329218729Shselasky typedef NodeVector::iterator roots_iterator; 330218729Shselasky typedef NodeVector::const_iterator const_roots_iterator; 331218729Shselasky typedef NodeVector::iterator eop_iterator; 332218729Shselasky typedef NodeVector::const_iterator const_eop_iterator; 333218729Shselasky typedef AllNodesTy::iterator node_iterator; 334218729Shselasky typedef AllNodesTy::const_iterator const_node_iterator; 335218729Shselasky 336218729Shselasky node_iterator nodes_begin() { return Nodes.begin(); } 337218729Shselasky 338218729Shselasky node_iterator nodes_end() { return Nodes.end(); } 339218729Shselasky 340218729Shselasky const_node_iterator nodes_begin() const { return Nodes.begin(); } 341218729Shselasky 342218729Shselasky const_node_iterator nodes_end() const { return Nodes.end(); } 343218729Shselasky 344218729Shselasky roots_iterator roots_begin() { return Roots.begin(); } 345218729Shselasky 346218729Shselasky roots_iterator roots_end() { return Roots.end(); } 347218729Shselasky 348218729Shselasky const_roots_iterator roots_begin() const { return Roots.begin(); } 349218729Shselasky 350218729Shselasky const_roots_iterator roots_end() const { return Roots.end(); } 351218729Shselasky 352218729Shselasky eop_iterator eop_begin() { return EndNodes.begin(); } 353218729Shselasky 354218729Shselasky eop_iterator eop_end() { return EndNodes.end(); } 355218729Shselasky 356218729Shselasky const_eop_iterator eop_begin() const { return EndNodes.begin(); } 357218729Shselasky 358218729Shselasky const_eop_iterator eop_end() const { return EndNodes.end(); } 359218729Shselasky 360218729Shselasky llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } 361218729Shselasky BumpVectorContext &getNodeAllocator() { return BVC; } 362218729Shselasky 363218729Shselasky typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> NodeMap; 364218729Shselasky 365218729Shselasky /// Creates a trimmed version of the graph that only contains paths leading 366218729Shselasky /// to the given nodes. 367218729Shselasky /// 368218729Shselasky /// \param Nodes The nodes which must appear in the final graph. Presumably 369218729Shselasky /// these are end-of-path nodes (i.e. they have no successors). 370218729Shselasky /// \param[out] ForwardMap A optional map from nodes in this graph to nodes in 371218729Shselasky /// the returned graph. 372218729Shselasky /// \param[out] InverseMap An optional map from nodes in the returned graph to 373218729Shselasky /// nodes in this graph. 374218729Shselasky /// \returns The trimmed graph 375218729Shselasky ExplodedGraph *trim(ArrayRef<const NodeTy *> Nodes, 376218729Shselasky InterExplodedGraphMap *ForwardMap = nullptr, 377218729Shselasky InterExplodedGraphMap *InverseMap = nullptr) const; 378218729Shselasky 379218729Shselasky /// Enable tracking of recently allocated nodes for potential reclamation 380218729Shselasky /// when calling reclaimRecentlyAllocatedNodes(). 381218729Shselasky void enableNodeReclamation(unsigned Interval) { 382218729Shselasky ReclaimCounter = ReclaimNodeInterval = Interval; 383218729Shselasky } 384218729Shselasky 385218729Shselasky /// Reclaim "uninteresting" nodes created since the last time this method 386218729Shselasky /// was called. 387218729Shselasky void reclaimRecentlyAllocatedNodes(); 388218729Shselasky 389218729Shselasky /// \brief Returns true if nodes for the given expression kind are always 390218729Shselasky /// kept around. 391218729Shselasky static bool isInterestingLValueExpr(const Expr *Ex); 392218729Shselasky 393218729Shselaskyprivate: 394218729Shselasky bool shouldCollect(const ExplodedNode *node); 395218729Shselasky void collectNode(ExplodedNode *node); 396218729Shselasky}; 397218729Shselasky 398218729Shselaskyclass ExplodedNodeSet { 399218729Shselasky typedef llvm::SmallPtrSet<ExplodedNode*,5> ImplTy; 400218729Shselasky ImplTy Impl; 401218729Shselasky 402218729Shselaskypublic: 403218729Shselasky ExplodedNodeSet(ExplodedNode *N) { 404218729Shselasky assert (N && !static_cast<ExplodedNode*>(N)->isSink()); 405218729Shselasky Impl.insert(N); 406218729Shselasky } 407218729Shselasky 408218729Shselasky ExplodedNodeSet() {} 409218729Shselasky 410218729Shselasky inline void Add(ExplodedNode *N) { 411218729Shselasky if (N && !static_cast<ExplodedNode*>(N)->isSink()) Impl.insert(N); 412218729Shselasky } 413218729Shselasky 414218729Shselasky typedef ImplTy::iterator iterator; 415218729Shselasky typedef ImplTy::const_iterator const_iterator; 416218729Shselasky 417218729Shselasky unsigned size() const { return Impl.size(); } 418218729Shselasky bool empty() const { return Impl.empty(); } 419218729Shselasky bool erase(ExplodedNode *N) { return Impl.erase(N); } 420218729Shselasky 421218729Shselasky void clear() { Impl.clear(); } 422218729Shselasky void insert(const ExplodedNodeSet &S) { 423218729Shselasky assert(&S != this); 424218729Shselasky if (empty()) 425218729Shselasky Impl = S.Impl; 426218729Shselasky else 427218729Shselasky Impl.insert(S.begin(), S.end()); 428218729Shselasky } 429218729Shselasky 430218729Shselasky inline iterator begin() { return Impl.begin(); } 431218729Shselasky inline iterator end() { return Impl.end(); } 432218729Shselasky 433218729Shselasky inline const_iterator begin() const { return Impl.begin(); } 434218729Shselasky inline const_iterator end() const { return Impl.end(); } 435218729Shselasky}; 436218729Shselasky 437218729Shselasky} // end GR namespace 438218729Shselasky 439218729Shselasky} // end clang namespace 440218729Shselasky 441218729Shselasky// GraphTraits 442218729Shselasky 443218729Shselaskynamespace llvm { 444218729Shselasky template<> struct GraphTraits<clang::ento::ExplodedNode*> { 445218729Shselasky typedef clang::ento::ExplodedNode NodeType; 446218729Shselasky typedef NodeType::succ_iterator ChildIteratorType; 447218729Shselasky typedef llvm::df_iterator<NodeType*> nodes_iterator; 448218729Shselasky 449218729Shselasky static inline NodeType* getEntryNode(NodeType* N) { 450218729Shselasky return N; 451218729Shselasky } 452218729Shselasky 453218729Shselasky static inline ChildIteratorType child_begin(NodeType* N) { 454218729Shselasky return N->succ_begin(); 455218729Shselasky } 456218729Shselasky 457218729Shselasky static inline ChildIteratorType child_end(NodeType* N) { 458218729Shselasky return N->succ_end(); 459218729Shselasky } 460218729Shselasky 461218729Shselasky static inline nodes_iterator nodes_begin(NodeType* N) { 462218729Shselasky return df_begin(N); 463218729Shselasky } 464218729Shselasky 465218729Shselasky static inline nodes_iterator nodes_end(NodeType* N) { 466218729Shselasky return df_end(N); 467218729Shselasky } 468218729Shselasky }; 469218729Shselasky 470218729Shselasky template<> struct GraphTraits<const clang::ento::ExplodedNode*> { 471218729Shselasky typedef const clang::ento::ExplodedNode NodeType; 472218729Shselasky typedef NodeType::const_succ_iterator ChildIteratorType; 473218729Shselasky typedef llvm::df_iterator<NodeType*> nodes_iterator; 474218729Shselasky 475218729Shselasky static inline NodeType* getEntryNode(NodeType* N) { 476218729Shselasky return N; 477218729Shselasky } 478218729Shselasky 479218729Shselasky static inline ChildIteratorType child_begin(NodeType* N) { 480218729Shselasky return N->succ_begin(); 481218729Shselasky } 482218729Shselasky 483218729Shselasky static inline ChildIteratorType child_end(NodeType* N) { 484218729Shselasky return N->succ_end(); 485218729Shselasky } 486218729Shselasky 487218729Shselasky static inline nodes_iterator nodes_begin(NodeType* N) { 488218729Shselasky return df_begin(N); 489218729Shselasky } 490218729Shselasky 491218729Shselasky static inline nodes_iterator nodes_end(NodeType* N) { 492218729Shselasky return df_end(N); 493218729Shselasky } 494218729Shselasky }; 495218729Shselasky 496218729Shselasky} // end llvm namespace 497218729Shselasky 498218729Shselasky#endif 499218729Shselasky