1 //===-- Graph.h - XRay Graph Class ------------------------------*- 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 // A Graph Datatype for XRay. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_XRAY_GRAPH_H 14 #define LLVM_XRAY_GRAPH_H 15 16 #include <initializer_list> 17 #include <stdint.h> 18 #include <type_traits> 19 #include <utility> 20 21 #include "llvm/ADT/DenseMap.h" 22 #include "llvm/ADT/DenseSet.h" 23 #include "llvm/ADT/iterator.h" 24 #include "llvm/Support/Error.h" 25 26 namespace llvm { 27 namespace xray { 28 29 /// A Graph object represents a Directed Graph and is used in XRay to compute 30 /// and store function call graphs and associated statistical information. 31 /// 32 /// The graph takes in four template parameters, these are: 33 /// - VertexAttribute, this is a structure which is stored for each vertex. 34 /// Must be DefaultConstructible, CopyConstructible, CopyAssignable and 35 /// Destructible. 36 /// - EdgeAttribute, this is a structure which is stored for each edge 37 /// Must be DefaultConstructible, CopyConstructible, CopyAssignable and 38 /// Destructible. 39 /// - EdgeAttribute, this is a structure which is stored for each variable 40 /// - VI, this is a type over which DenseMapInfo is defined and is the type 41 /// used look up strings, available as VertexIdentifier. 42 /// - If the built in DenseMapInfo is not defined, provide a specialization 43 /// class type here. 44 /// 45 /// Graph is CopyConstructible, CopyAssignable, MoveConstructible and 46 /// MoveAssignable but is not EqualityComparible or LessThanComparible. 47 /// 48 /// Usage Example Graph with weighted edges and vertices: 49 /// Graph<int, int, int> G; 50 /// 51 /// G[1] = 0; 52 /// G[2] = 2; 53 /// G[{1,2}] = 1; 54 /// G[{2,1}] = -1; 55 /// for(const auto &v : G.vertices()){ 56 /// // Do something with the vertices in the graph; 57 /// } 58 /// for(const auto &e : G.edges()){ 59 /// // Do something with the edges in the graph; 60 /// } 61 /// 62 /// Usage Example with StrRef keys. 63 /// Graph<int, double, StrRef> StrG; 64 /// char va[] = "Vertex A"; 65 /// char vaa[] = "Vertex A"; 66 /// char vb[] = "Vertex B"; // Vertices are referenced by String Refs. 67 /// G[va] = 0; 68 /// G[vb] = 1; 69 /// G[{va, vb}] = 1.0; 70 /// cout() << G[vaa] << " " << G[{vaa, vb}]; //prints "0 1.0". 71 /// 72 template <typename VertexAttribute, typename EdgeAttribute, 73 typename VI = int32_t> 74 class Graph { 75 public: 76 /// These objects are used to name edges and vertices in the graph. 77 typedef VI VertexIdentifier; 78 typedef std::pair<VI, VI> EdgeIdentifier; 79 80 /// This type is the value_type of all iterators which range over vertices, 81 /// Determined by the Vertices DenseMap 82 using VertexValueType = 83 detail::DenseMapPair<VertexIdentifier, VertexAttribute>; 84 85 /// This type is the value_type of all iterators which range over edges, 86 /// Determined by the Edges DenseMap. 87 using EdgeValueType = detail::DenseMapPair<EdgeIdentifier, EdgeAttribute>; 88 89 using size_type = std::size_t; 90 91 private: 92 /// The type used for storing the EdgeAttribute for each edge in the graph 93 using EdgeMapT = DenseMap<EdgeIdentifier, EdgeAttribute>; 94 95 /// The type used for storing the VertexAttribute for each vertex in 96 /// the graph. 97 using VertexMapT = DenseMap<VertexIdentifier, VertexAttribute>; 98 99 /// The type used for storing the edges entering a vertex. Indexed by 100 /// the VertexIdentifier of the start of the edge. Only used to determine 101 /// where the incoming edges are, the EdgeIdentifiers are stored in an 102 /// InnerEdgeMapT. 103 using NeighborSetT = DenseSet<VertexIdentifier>; 104 105 /// The type storing the InnerInvGraphT corresponding to each vertex in 106 /// the graph (When a vertex has an incoming edge incident to it) 107 using NeighborLookupT = DenseMap<VertexIdentifier, NeighborSetT>; 108 109 private: 110 /// Stores the map from the start and end vertex of an edge to it's 111 /// EdgeAttribute 112 EdgeMapT Edges; 113 114 /// Stores the map from VertexIdentifier to VertexAttribute 115 VertexMapT Vertices; 116 117 /// Allows fast lookup for the incoming edge set of any given vertex. 118 NeighborLookupT InNeighbors; 119 120 /// Allows fast lookup for the outgoing edge set of any given vertex. 121 NeighborLookupT OutNeighbors; 122 123 /// An Iterator adapter using an InnerInvGraphT::iterator as a base iterator, 124 /// and storing the VertexIdentifier the iterator range comes from. The 125 /// dereference operator is then performed using a pointer to the graph's edge 126 /// set. 127 template <bool IsConst, bool IsOut, 128 typename BaseIt = typename NeighborSetT::const_iterator, 129 typename T = 130 std::conditional_t<IsConst, const EdgeValueType, EdgeValueType>> 131 class NeighborEdgeIteratorT 132 : public iterator_adaptor_base< 133 NeighborEdgeIteratorT<IsConst, IsOut>, BaseIt, 134 typename std::iterator_traits<BaseIt>::iterator_category, T> { 135 using InternalEdgeMapT = 136 std::conditional_t<IsConst, const EdgeMapT, EdgeMapT>; 137 138 friend class NeighborEdgeIteratorT<false, IsOut, BaseIt, EdgeValueType>; 139 friend class NeighborEdgeIteratorT<true, IsOut, BaseIt, 140 const EdgeValueType>; 141 142 InternalEdgeMapT *MP; 143 VertexIdentifier SI; 144 145 public: 146 template <bool IsConstDest, 147 typename = std::enable_if_t<IsConstDest && !IsConst>> 148 operator NeighborEdgeIteratorT<IsConstDest, IsOut, BaseIt, 149 const EdgeValueType>() const { 150 return NeighborEdgeIteratorT<IsConstDest, IsOut, BaseIt, 151 const EdgeValueType>(this->I, MP, SI); 152 } 153 154 NeighborEdgeIteratorT() = default; 155 NeighborEdgeIteratorT(BaseIt _I, InternalEdgeMapT *_MP, 156 VertexIdentifier _SI) 157 : iterator_adaptor_base< 158 NeighborEdgeIteratorT<IsConst, IsOut>, BaseIt, 159 typename std::iterator_traits<BaseIt>::iterator_category, T>(_I), 160 MP(_MP), SI(_SI) {} 161 162 T &operator*() const { 163 if (!IsOut) 164 return *(MP->find({*(this->I), SI})); 165 else 166 return *(MP->find({SI, *(this->I)})); 167 } 168 }; 169 170 public: 171 /// A const iterator type for iterating through the set of edges entering a 172 /// vertex. 173 /// 174 /// Has a const EdgeValueType as its value_type 175 using ConstInEdgeIterator = NeighborEdgeIteratorT<true, false>; 176 177 /// An iterator type for iterating through the set of edges leaving a vertex. 178 /// 179 /// Has an EdgeValueType as its value_type 180 using InEdgeIterator = NeighborEdgeIteratorT<false, false>; 181 182 /// A const iterator type for iterating through the set of edges entering a 183 /// vertex. 184 /// 185 /// Has a const EdgeValueType as its value_type 186 using ConstOutEdgeIterator = NeighborEdgeIteratorT<true, true>; 187 188 /// An iterator type for iterating through the set of edges leaving a vertex. 189 /// 190 /// Has an EdgeValueType as its value_type 191 using OutEdgeIterator = NeighborEdgeIteratorT<false, true>; 192 193 /// A class for ranging over the incoming edges incident to a vertex. 194 /// 195 /// Like all views in this class it provides methods to get the beginning and 196 /// past the range iterators for the range, as well as methods to determine 197 /// the number of elements in the range and whether the range is empty. 198 template <bool isConst, bool isOut> class InOutEdgeView { 199 public: 200 using iterator = NeighborEdgeIteratorT<isConst, isOut>; 201 using const_iterator = NeighborEdgeIteratorT<true, isOut>; 202 using GraphT = std::conditional_t<isConst, const Graph, Graph>; 203 using InternalEdgeMapT = 204 std::conditional_t<isConst, const EdgeMapT, EdgeMapT>; 205 206 private: 207 InternalEdgeMapT &M; 208 const VertexIdentifier A; 209 const NeighborLookupT &NL; 210 211 public: 212 iterator begin() { 213 auto It = NL.find(A); 214 if (It == NL.end()) 215 return iterator(); 216 return iterator(It->second.begin(), &M, A); 217 } 218 219 const_iterator cbegin() const { 220 auto It = NL.find(A); 221 if (It == NL.end()) 222 return const_iterator(); 223 return const_iterator(It->second.begin(), &M, A); 224 } 225 226 const_iterator begin() const { return cbegin(); } 227 228 iterator end() { 229 auto It = NL.find(A); 230 if (It == NL.end()) 231 return iterator(); 232 return iterator(It->second.end(), &M, A); 233 } 234 const_iterator cend() const { 235 auto It = NL.find(A); 236 if (It == NL.end()) 237 return const_iterator(); 238 return const_iterator(It->second.end(), &M, A); 239 } 240 241 const_iterator end() const { return cend(); } 242 243 size_type size() const { 244 auto I = NL.find(A); 245 if (I == NL.end()) 246 return 0; 247 else 248 return I->second.size(); 249 } 250 251 bool empty() const { return NL.count(A) == 0; }; 252 253 InOutEdgeView(GraphT &G, VertexIdentifier A) 254 : M(G.Edges), A(A), NL(isOut ? G.OutNeighbors : G.InNeighbors) {} 255 }; 256 257 /// A const iterator type for iterating through the whole vertex set of the 258 /// graph. 259 /// 260 /// Has a const VertexValueType as its value_type 261 using ConstVertexIterator = typename VertexMapT::const_iterator; 262 263 /// An iterator type for iterating through the whole vertex set of the graph. 264 /// 265 /// Has a VertexValueType as its value_type 266 using VertexIterator = typename VertexMapT::iterator; 267 268 /// A class for ranging over the vertices in the graph. 269 /// 270 /// Like all views in this class it provides methods to get the beginning and 271 /// past the range iterators for the range, as well as methods to determine 272 /// the number of elements in the range and whether the range is empty. 273 template <bool isConst> class VertexView { 274 public: 275 using iterator = 276 std::conditional_t<isConst, ConstVertexIterator, VertexIterator>; 277 using const_iterator = ConstVertexIterator; 278 using GraphT = std::conditional_t<isConst, const Graph, Graph>; 279 280 private: 281 GraphT &G; 282 283 public: 284 iterator begin() { return G.Vertices.begin(); } 285 iterator end() { return G.Vertices.end(); } 286 const_iterator cbegin() const { return G.Vertices.cbegin(); } 287 const_iterator cend() const { return G.Vertices.cend(); } 288 const_iterator begin() const { return G.Vertices.begin(); } 289 const_iterator end() const { return G.Vertices.end(); } 290 size_type size() const { return G.Vertices.size(); } 291 bool empty() const { return G.Vertices.empty(); } 292 VertexView(GraphT &_G) : G(_G) {} 293 }; 294 295 /// A const iterator for iterating through the entire edge set of the graph. 296 /// 297 /// Has a const EdgeValueType as its value_type 298 using ConstEdgeIterator = typename EdgeMapT::const_iterator; 299 300 /// An iterator for iterating through the entire edge set of the graph. 301 /// 302 /// Has an EdgeValueType as its value_type 303 using EdgeIterator = typename EdgeMapT::iterator; 304 305 /// A class for ranging over all the edges in the graph. 306 /// 307 /// Like all views in this class it provides methods to get the beginning and 308 /// past the range iterators for the range, as well as methods to determine 309 /// the number of elements in the range and whether the range is empty. 310 template <bool isConst> class EdgeView { 311 public: 312 using iterator = 313 std::conditional_t<isConst, ConstEdgeIterator, EdgeIterator>; 314 using const_iterator = ConstEdgeIterator; 315 using GraphT = std::conditional_t<isConst, const Graph, Graph>; 316 317 private: 318 GraphT &G; 319 320 public: 321 iterator begin() { return G.Edges.begin(); } 322 iterator end() { return G.Edges.end(); } 323 const_iterator cbegin() const { return G.Edges.cbegin(); } 324 const_iterator cend() const { return G.Edges.cend(); } 325 const_iterator begin() const { return G.Edges.begin(); } 326 const_iterator end() const { return G.Edges.end(); } 327 size_type size() const { return G.Edges.size(); } 328 bool empty() const { return G.Edges.empty(); } 329 EdgeView(GraphT &_G) : G(_G) {} 330 }; 331 332 public: 333 // TODO: implement constructor to enable Graph Initialisation.\ 334 // Something like: 335 // Graph<int, int, int> G( 336 // {1, 2, 3, 4, 5}, 337 // {{1, 2}, {2, 3}, {3, 4}}); 338 339 /// Empty the Graph 340 void clear() { 341 Edges.clear(); 342 Vertices.clear(); 343 InNeighbors.clear(); 344 OutNeighbors.clear(); 345 } 346 347 /// Returns a view object allowing iteration over the vertices of the graph. 348 /// also allows access to the size of the vertex set. 349 VertexView<false> vertices() { return VertexView<false>(*this); } 350 351 VertexView<true> vertices() const { return VertexView<true>(*this); } 352 353 /// Returns a view object allowing iteration over the edges of the graph. 354 /// also allows access to the size of the edge set. 355 EdgeView<false> edges() { return EdgeView<false>(*this); } 356 357 EdgeView<true> edges() const { return EdgeView<true>(*this); } 358 359 /// Returns a view object allowing iteration over the edges which start at 360 /// a vertex I. 361 InOutEdgeView<false, true> outEdges(const VertexIdentifier I) { 362 return InOutEdgeView<false, true>(*this, I); 363 } 364 365 InOutEdgeView<true, true> outEdges(const VertexIdentifier I) const { 366 return InOutEdgeView<true, true>(*this, I); 367 } 368 369 /// Returns a view object allowing iteration over the edges which point to 370 /// a vertex I. 371 InOutEdgeView<false, false> inEdges(const VertexIdentifier I) { 372 return InOutEdgeView<false, false>(*this, I); 373 } 374 375 InOutEdgeView<true, false> inEdges(const VertexIdentifier I) const { 376 return InOutEdgeView<true, false>(*this, I); 377 } 378 379 /// Looks up the vertex with identifier I, if it does not exist it default 380 /// constructs it. 381 VertexAttribute &operator[](const VertexIdentifier &I) { return Vertices[I]; } 382 383 /// Looks up the edge with identifier I, if it does not exist it default 384 /// constructs it, if it's endpoints do not exist it also default constructs 385 /// them. 386 EdgeAttribute &operator[](const EdgeIdentifier &I) { 387 Vertices.try_emplace(I.first); 388 Vertices.try_emplace(I.second); 389 InNeighbors[I.second].insert(I.first); 390 OutNeighbors[I.first].insert(I.second); 391 return Edges[I]; 392 } 393 394 /// Looks up a vertex with Identifier I, or an error if it does not exist. 395 Expected<VertexAttribute &> at(const VertexIdentifier &I) { 396 auto It = Vertices.find(I); 397 if (It == Vertices.end()) 398 return make_error<StringError>( 399 "Vertex Identifier Does Not Exist", 400 std::make_error_code(std::errc::invalid_argument)); 401 return It->second; 402 } 403 404 Expected<const VertexAttribute &> at(const VertexIdentifier &I) const { 405 auto It = Vertices.find(I); 406 if (It == Vertices.end()) 407 return make_error<StringError>( 408 "Vertex Identifier Does Not Exist", 409 std::make_error_code(std::errc::invalid_argument)); 410 return It->second; 411 } 412 413 /// Looks up an edge with Identifier I, or an error if it does not exist. 414 Expected<EdgeAttribute &> at(const EdgeIdentifier &I) { 415 auto It = Edges.find(I); 416 if (It == Edges.end()) 417 return make_error<StringError>( 418 "Edge Identifier Does Not Exist", 419 std::make_error_code(std::errc::invalid_argument)); 420 return It->second; 421 } 422 423 Expected<const EdgeAttribute &> at(const EdgeIdentifier &I) const { 424 auto It = Edges.find(I); 425 if (It == Edges.end()) 426 return make_error<StringError>( 427 "Edge Identifier Does Not Exist", 428 std::make_error_code(std::errc::invalid_argument)); 429 return It->second; 430 } 431 432 /// Looks for a vertex with identifier I, returns 1 if one exists, and 433 /// 0 otherwise 434 size_type count(const VertexIdentifier &I) const { 435 return Vertices.count(I); 436 } 437 438 /// Looks for an edge with Identifier I, returns 1 if one exists and 0 439 /// otherwise 440 size_type count(const EdgeIdentifier &I) const { return Edges.count(I); } 441 442 /// Inserts a vertex into the graph with Identifier Val.first, and 443 /// Attribute Val.second. 444 std::pair<VertexIterator, bool> 445 insert(const std::pair<VertexIdentifier, VertexAttribute> &Val) { 446 return Vertices.insert(Val); 447 } 448 449 std::pair<VertexIterator, bool> 450 insert(std::pair<VertexIdentifier, VertexAttribute> &&Val) { 451 return Vertices.insert(std::move(Val)); 452 } 453 454 /// Inserts an edge into the graph with Identifier Val.first, and 455 /// Attribute Val.second. If the key is already in the map, it returns false 456 /// and doesn't update the value. 457 std::pair<EdgeIterator, bool> 458 insert(const std::pair<EdgeIdentifier, EdgeAttribute> &Val) { 459 const auto &p = Edges.insert(Val); 460 if (p.second) { 461 const auto &EI = Val.first; 462 Vertices.FindAndConstruct(EI.first); 463 Vertices.FindAndConstruct(EI.second); 464 InNeighbors[EI.second].insert(EI.first); 465 OutNeighbors[EI.first].insert(EI.second); 466 }; 467 468 return p; 469 } 470 471 /// Inserts an edge into the graph with Identifier Val.first, and 472 /// Attribute Val.second. If the key is already in the map, it returns false 473 /// and doesn't update the value. 474 std::pair<EdgeIterator, bool> 475 insert(std::pair<EdgeIdentifier, EdgeAttribute> &&Val) { 476 auto EI = Val.first; 477 const auto &p = Edges.insert(std::move(Val)); 478 if (p.second) { 479 Vertices.try_emplace(EI.first); 480 Vertices.try_emplace(EI.second); 481 InNeighbors[EI.second].insert(EI.first); 482 OutNeighbors[EI.first].insert(EI.second); 483 }; 484 485 return p; 486 } 487 }; 488 } 489 } 490 #endif 491