GTIRB  v2.1.0
GrammaTech Intermediate Representation for Binaries: C++ API
CFG.hpp
Go to the documentation of this file.
1 //===- CFG.hpp --------------------------------------------------*- C++ -*-===//
2 //
3 // Copyright (C) 2021 GrammaTech, Inc.
4 //
5 // This code is licensed under the MIT license. See the LICENSE file in the
6 // project root for license terms.
7 //
8 // This project is sponsored by the Office of Naval Research, One Liberty
9 // Center, 875 N. Randolph Street, Arlington, VA 22203 under contract #
10 // N68335-17-C-0700. The content of the information does not necessarily
11 // reflect the position or policy of the Government and no official
12 // endorsement should be inferred.
13 //
14 //===----------------------------------------------------------------------===//
15 #ifndef GTIRB_CFG_H
16 #define GTIRB_CFG_H
17 
18 #include <gtirb/Casting.hpp>
19 #include <gtirb/Export.hpp>
20 #include <boost/graph/adjacency_list.hpp>
21 #include <boost/iterator/filter_iterator.hpp>
22 #include <boost/iterator/indirect_iterator.hpp>
23 #include <boost/iterator/iterator_facade.hpp>
24 #include <boost/iterator/transform_iterator.hpp>
25 #include <boost/range/iterator_range.hpp>
26 #include <variant>
27 
34 
35 namespace gtirb {
36 class CfgNode;
37 class CodeBlock;
38 
46 
49 enum class ConditionalEdge : bool {
50  OnFalse,
51  OnTrue
53 };
55 GTIRB_EXPORT_API std::ostream& operator<<(std::ostream& OS,
56  const ConditionalEdge& CE);
57 
60 enum class DirectEdge : bool { IsIndirect, IsDirect };
61 GTIRB_EXPORT_API std::ostream& operator<<(std::ostream& OS,
62  const DirectEdge& DE);
63 
67 GTIRB_EXPORT_API std::ostream& operator<<(std::ostream& OS, const EdgeType& ET);
68 
71 using EdgeLabel =
72  std::optional<std::tuple<ConditionalEdge, DirectEdge, EdgeType>>;
73 GTIRB_EXPORT_API std::ostream& operator<<(std::ostream& OS,
74  const EdgeLabel& Label);
76 
77 // Helper for constructing the CFG type. The graph property needs to refer to
78 // the graph's vertex_descriptor type. This is accessible via
79 // boost::adjacency_list_traits, but requires keeping the template parameters
80 // for boost::adjacency_list and boost::adjacency_list_traits in sync. This
81 // helper ensures the relevant parameters are the same for both.
82 template <class OutEdgeListS = boost::vecS, class VertexListS = boost::vecS,
83  class DirectedS = boost::directedS, class EdgeListS = boost::listS>
84 struct CfgBuilder {
85  using vertex_descriptor = typename boost::adjacency_list_traits<
86  OutEdgeListS, VertexListS, DirectedS, EdgeListS>::vertex_descriptor;
87  using type = boost::adjacency_list<
88  OutEdgeListS, VertexListS, DirectedS,
89  // Vertices are CfgNodes.
90  CfgNode*,
91  // Edges have labels.
92  EdgeLabel,
93  // The graph keeps track of vertex descriptors for
94  // each node.
95  std::unordered_map<const CfgNode*, vertex_descriptor>, EdgeListS>;
96 };
98 
102 using CFG = CfgBuilder<boost::listS, // allow parallel edges
103  boost::listS, // preserve IDs after mutations
104  boost::bidirectionalS // successor and predecessor edges
105  >::type;
107 class cfg_node_iter_base
108  : public boost::iterator_facade<cfg_node_iter_base,
109  const boost::vertex_bundle_type<CFG>::type,
110  CFG::vertex_iterator::iterator_category> {
111 public:
112  cfg_node_iter_base() = default;
113  cfg_node_iter_base(const CFG& cfg_, CFG::vertex_iterator& it_)
114  : cfg(&cfg_), it(it_) {}
115 
116  // Use default move and copy constructors and assignment operators.
117  cfg_node_iter_base(const cfg_node_iter_base&) = default;
118  cfg_node_iter_base(cfg_node_iter_base&&) = default;
119  cfg_node_iter_base& operator=(const cfg_node_iter_base&) = default;
120  cfg_node_iter_base& operator=(cfg_node_iter_base&&) = default;
121 
122 private:
123  friend class boost::iterator_core_access;
124 
125  void increment() { ++it; }
126  void decrement() { --it; }
127 
128  std::ptrdiff_t distance_to(const cfg_node_iter_base& other) const {
129  return std::distance(it, other.it);
130  }
131 
132  bool equal(const cfg_node_iter_base& other) const { return it == other.it; }
133 
134  const boost::vertex_bundle_type<CFG>::type& dereference() const {
135  return (*cfg)[*it];
136  }
137 
138  const CFG* cfg{nullptr};
139  CFG::vertex_iterator it;
140 };
141 
142 template <typename ToTy> struct downcast {
143  template <typename FromTy> auto operator()(FromTy& Val) const {
144  return dyn_cast_or_null<ToTy>(Val);
145  }
146 };
147 
148 struct not_null {
149  template <typename T> bool operator()(const T* t) { return t != nullptr; }
150 };
151 
152 template <typename T>
153 using cfg_node_downcast_iter =
154  boost::transform_iterator<downcast<std::remove_const_t<T>>,
155  cfg_node_iter_base>;
156 
157 template <typename T>
158 using cfg_node_downcast_not_null_iter =
159  boost::filter_iterator<not_null, cfg_node_downcast_iter<T>>;
160 
161 template <typename T>
162 class cfg_node_cast_iter
163  : public boost::indirect_iterator<cfg_node_downcast_not_null_iter<T>, T> {
164 private:
165  using xform_iterator = cfg_node_downcast_iter<T>;
166  using filter_iterator = cfg_node_downcast_not_null_iter<T>;
167  using parent = boost::indirect_iterator<filter_iterator, T>;
168 
169 public:
170  cfg_node_cast_iter() : parent() {}
171 
172  cfg_node_cast_iter(const CFG& g, CFG::vertex_iterator& first,
173  CFG::vertex_iterator& last)
174  : parent(filter_iterator(xform_iterator(cfg_node_iter_base(g, first)),
175  xform_iterator(cfg_node_iter_base(g, last)))) {}
176 
177  template <typename OtherT>
178  cfg_node_cast_iter(const cfg_node_cast_iter<OtherT>& other) : parent(other) {}
179 };
180 
182 
185 using cfg_iterator = boost::indirect_iterator<cfg_node_iter_base>;
186 
189 using const_cfg_iterator =
190  boost::indirect_iterator<cfg_node_iter_base, const CfgNode>;
191 
194 using block_iterator = cfg_node_cast_iter<CodeBlock>;
195 
198 using const_block_iterator = cfg_node_cast_iter<const CodeBlock>;
199 
210 GTIRB_EXPORT_API std::pair<CFG::vertex_descriptor, bool> addVertex(CfgNode* B,
211  CFG& Cfg);
212 
223 
232 GTIRB_EXPORT_API std::optional<CFG::vertex_descriptor>
233 getVertex(const CfgNode* N, const CFG& Cfg);
234 
245 GTIRB_EXPORT_API std::optional<CFG::edge_descriptor>
246 addEdge(const CfgNode* From, const CfgNode* To, CFG& Cfg);
247 
258 GTIRB_EXPORT_API bool removeEdge(const CfgNode* From, const CfgNode* To,
259  CFG& Cfg);
260 
273 GTIRB_EXPORT_API bool removeEdge(const CfgNode* From, const CfgNode* To,
274  EdgeLabel Label, CFG& Cfg);
275 
282 GTIRB_EXPORT_API boost::iterator_range<cfg_iterator> nodes(CFG& Cfg);
283 
291 GTIRB_EXPORT_API boost::iterator_range<const_cfg_iterator>
292 nodes(const CFG& Cfg);
293 
303 GTIRB_EXPORT_API boost::iterator_range<block_iterator> blocks(CFG& Cfg);
304 
315 GTIRB_EXPORT_API boost::iterator_range<const_block_iterator>
316 blocks(const CFG& Cfg);
317 
319 // Traits for instantiating cfgEdgeIters as cfgPredecessors.
320 struct CfgPredecessorTraits {
321  using edge_iterator = boost::graph_traits<CFG>::in_edge_iterator;
322  static CfgNode* getNode(const CFG::edge_descriptor& EDesc, const CFG& Cfg) {
323  return Cfg[source(EDesc, Cfg)];
324  }
325  static std::pair<edge_iterator, edge_iterator>
326  getEdges(const CFG::vertex_descriptor& VtxDescr, const CFG& Cfg) {
327  return in_edges(VtxDescr, Cfg);
328  }
329 };
331 
333 // Traits for instantiating cfgEdgeIters as cfgSuccessors.
334 struct CfgSuccessorTraits {
335  using edge_iterator = boost::graph_traits<CFG>::out_edge_iterator;
336  static CfgNode* getNode(const CFG::edge_descriptor& EDesc, const CFG& Cfg) {
337  return Cfg[target(EDesc, Cfg)];
338  }
339  static std::pair<edge_iterator, edge_iterator>
340  getEdges(const CFG::vertex_descriptor& VtxDescr, const CFG& Cfg) {
341  return out_edges(VtxDescr, Cfg);
342  }
343 };
345 
351 template <class Traits, typename CfgNodePtr> struct EdgeDescrToNodeLabel {
352  // Yes, this stores a const CFG* even for the version that returns a
353  // non-const CfgNode. We can do this because the constness of the two are
354  // actually independent, though we maintain the illusion of their related
355  // constness in the public interface functions cfgPredecessors and
356  // cfgSuccessors.
357  const CFG* Cfg = nullptr; // Should only be null for end iterator
358 
359  EdgeDescrToNodeLabel() = default;
360  EdgeDescrToNodeLabel(const CFG* G) : Cfg(G) {}
361  // This quasi-copy constructor enables conversion from non-const iterator to
362  // const iterator, but not vice versa.
363  EdgeDescrToNodeLabel(const EdgeDescrToNodeLabel<Traits, CfgNode*>& Rhs)
364  : Cfg(Rhs.Cfg) {}
365 
366  std::pair<CfgNodePtr, EdgeLabel>
367  operator()(const CFG::edge_descriptor& EDesc) const {
368  return {Traits::getNode(EDesc, *Cfg), (*Cfg)[EDesc]};
369  }
370 };
372 
387 template <class Traits, typename CfgNodePtr>
388 auto cfgEdgeIters(const CFG& G, const CfgNode* N) {
389  const std::optional<CFG::vertex_descriptor> OptVtxDescr = getVertex(N, G);
390  if (OptVtxDescr == std::nullopt) {
391  // FYI: this is the return type
392  return boost::iterator_range<
393  boost::transform_iterator<EdgeDescrToNodeLabel<Traits, CfgNodePtr>,
394  typename Traits::edge_iterator>>();
395  } else {
396  const auto [Begin, End] = Traits::getEdges(OptVtxDescr.value(), G);
397  return boost::make_iterator_range(
398  boost::make_transform_iterator(
399  Begin, EdgeDescrToNodeLabel<Traits, CfgNodePtr>{&G}),
400  boost::make_transform_iterator(
401  End, EdgeDescrToNodeLabel<Traits, CfgNodePtr>{&G}));
402  }
403 }
405 
407 using cfg_predecessors_range = boost::iterator_range<boost::transform_iterator<
408  EdgeDescrToNodeLabel<CfgPredecessorTraits, CfgNode*>,
409  CfgPredecessorTraits::edge_iterator>>;
412  boost::iterator_range<boost::transform_iterator<
413  EdgeDescrToNodeLabel<CfgPredecessorTraits, const CfgNode*>,
414  CfgPredecessorTraits::edge_iterator>>;
416 using cfg_successors_range = boost::iterator_range<boost::transform_iterator<
417  EdgeDescrToNodeLabel<CfgSuccessorTraits, CfgNode*>,
418  CfgSuccessorTraits::edge_iterator>>;
421  boost::iterator_range<boost::transform_iterator<
422  EdgeDescrToNodeLabel<CfgSuccessorTraits, const CfgNode*>,
423  CfgSuccessorTraits::edge_iterator>>;
424 
438  return cfgEdgeIters<CfgPredecessorTraits, CfgNode*>(G, N);
439 }
441  const CfgNode* N) {
442  return cfgEdgeIters<CfgPredecessorTraits, const CfgNode*>(G, N);
443 }
444 
458  return cfgEdgeIters<CfgSuccessorTraits, CfgNode*>(G, N);
459 }
461  const CfgNode* N) {
462  return cfgEdgeIters<CfgSuccessorTraits, const CfgNode*>(G, N);
463 }
464 
465 } // namespace gtirb
466 #endif // GTIRB_CFG_H
gtirb::EdgeType::Call
@ Call
gtirb::nodes
GTIRB_EXPORT_API boost::iterator_range< cfg_iterator > nodes(CFG &Cfg)
Get a range of the CfgNode elements in the specified graph.
gtirb::CfgNode
Represents the base of types that can be inserted into the CFG.
Definition: CfgNode.hpp:30
gtirb::DirectEdge::IsIndirect
@ IsIndirect
gtirb::DirectEdge::IsDirect
@ IsDirect
gtirb::cfgSuccessors
cfg_successors_range cfgSuccessors(CFG &G, const CfgNode *N)
Returns an iterator_range to iterate the successors of a CfgNode.
Definition: CFG.hpp:457
gtirb::const_block_iterator
cfg_node_cast_iter< const CodeBlock > const_block_iterator
Constant iterator over blocks (Block).
Definition: CFG.hpp:198
gtirb::getVertex
GTIRB_EXPORT_API std::optional< CFG::vertex_descriptor > getVertex(const CfgNode *N, const CFG &Cfg)
Get the boost::graph vertex descriptor for a CfgNode if it is in the graph.
gtirb::EdgeType
EdgeType
Indicates the type of control flow transfer indicated by this edge.
Definition: CFG.hpp:66
gtirb::EdgeType::Branch
@ Branch
gtirb::cfg_successors_range
boost::iterator_range< boost::transform_iterator< EdgeDescrToNodeLabel< CfgSuccessorTraits, CfgNode * >, CfgSuccessorTraits::edge_iterator > > cfg_successors_range
iterator_range over a CfgNode's successors, non-const version
Definition: CFG.hpp:418
GTIRB_EXPORT_API
#define GTIRB_EXPORT_API
This macro controls the visibility of exported symbols (i.e. classes) in shared libraries....
Definition: Export.hpp:52
gtirb
Main namespace for the GTIRB API.
Definition: Addr.hpp:28
Export.hpp
gtirb::cfgPredecessors
cfg_predecessors_range cfgPredecessors(CFG &G, const CfgNode *N)
Returns an iterator_range to iterate the predecessors of a CfgNode.
Definition: CFG.hpp:437
gtirb::block_iterator
cfg_node_cast_iter< CodeBlock > block_iterator
Iterator over blocks (Block).
Definition: CFG.hpp:194
gtirb::removeVertex
GTIRB_EXPORT_API bool removeVertex(CfgNode *N, CFG &Cfg)
Remove a node from the CFG.
gtirb::removeEdge
GTIRB_EXPORT_API bool removeEdge(const CfgNode *From, const CfgNode *To, CFG &Cfg)
Remove all edges between the source and target nodes from the CFG.
gtirb::ConditionalEdge::OnTrue
@ OnTrue
Indicates a conditional edge that fires when the condition is true.
gtirb::EdgeType::Sysret
@ Sysret
gtirb::EdgeType::Fallthrough
@ Fallthrough
gtirb::const_cfg_predecessors_range
boost::iterator_range< boost::transform_iterator< EdgeDescrToNodeLabel< CfgPredecessorTraits, const CfgNode * >, CfgPredecessorTraits::edge_iterator > > const_cfg_predecessors_range
iterator_range over a CfgNode's predecessors, const version
Definition: CFG.hpp:414
gtirb::ConditionalEdge
ConditionalEdge
Indicates whether an edge is conditional on true.
Definition: CFG.hpp:49
gtirb::EdgeType::Syscall
@ Syscall
Casting.hpp
The various casting and type checking operations that apply to gtirb::Node subclasses.
gtirb::cfg_predecessors_range
boost::iterator_range< boost::transform_iterator< EdgeDescrToNodeLabel< CfgPredecessorTraits, CfgNode * >, CfgPredecessorTraits::edge_iterator > > cfg_predecessors_range
iterator_range over a CfgNode's predecessors, non-const version
Definition: CFG.hpp:409
gtirb::cfg_iterator
boost::indirect_iterator< cfg_node_iter_base > cfg_iterator
Iterator over CfgNodes (CfgNode).
Definition: CFG.hpp:185
gtirb::DirectEdge
DirectEdge
Indicates whether an edge represents indirect control flow.
Definition: CFG.hpp:60
gtirb::ConditionalEdge::OnFalse
@ OnFalse
Indicates an unconditional edge or a conditional edge that fires when the condition is false.
gtirb::const_cfg_iterator
boost::indirect_iterator< cfg_node_iter_base, const CfgNode > const_cfg_iterator
Const iterator over CfgNodes (CfgNode).
Definition: CFG.hpp:190
gtirb::EdgeType::Return
@ Return
gtirb::addVertex
GTIRB_EXPORT_API std::pair< CFG::vertex_descriptor, bool > addVertex(CfgNode *B, CFG &Cfg)
Add a node to the CFG.
gtirb::operator<<
GTIRB_EXPORT_API std::ostream & operator<<(std::ostream &OS, const ConditionalEdge &CE)
gtirb::const_cfg_successors_range
boost::iterator_range< boost::transform_iterator< EdgeDescrToNodeLabel< CfgSuccessorTraits, const CfgNode * >, CfgSuccessorTraits::edge_iterator > > const_cfg_successors_range
iterator_range over a CfgNode's successors, const version
Definition: CFG.hpp:423
gtirb::CFG
CfgBuilder< boost::listS, boost::listS, boost::bidirectionalS >::type CFG
Interprocedural control flow graph, with vertices of type Block.
Definition: CFG.hpp:105
gtirb::blocks
GTIRB_EXPORT_API boost::iterator_range< block_iterator > blocks(CFG &Cfg)
Get a range of just the Block elements in the specified graph.
gtirb::addEdge
GTIRB_EXPORT_API std::optional< CFG::edge_descriptor > addEdge(const CfgNode *From, const CfgNode *To, CFG &Cfg)
Create a new edge between two CFG nodes if they exist in the graph.
gtirb::EdgeLabel
std::optional< std::tuple< ConditionalEdge, DirectEdge, EdgeType > > EdgeLabel
A label on a CFG edge.
Definition: CFG.hpp:72