1*0b57cec5SDimitry Andric //===-- sanitizer_bvgraph.h -------------------------------------*- C++ -*-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file is a part of Sanitizer runtime. 10*0b57cec5SDimitry Andric // BVGraph -- a directed graph. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #ifndef SANITIZER_BVGRAPH_H 15*0b57cec5SDimitry Andric #define SANITIZER_BVGRAPH_H 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric #include "sanitizer_common.h" 18*0b57cec5SDimitry Andric #include "sanitizer_bitvector.h" 19*0b57cec5SDimitry Andric 20*0b57cec5SDimitry Andric namespace __sanitizer { 21*0b57cec5SDimitry Andric 22*0b57cec5SDimitry Andric // Directed graph of fixed size implemented as an array of bit vectors. 23*0b57cec5SDimitry Andric // Not thread-safe, all accesses should be protected by an external lock. 24*0b57cec5SDimitry Andric template<class BV> 25*0b57cec5SDimitry Andric class BVGraph { 26*0b57cec5SDimitry Andric public: 27*0b57cec5SDimitry Andric enum SizeEnum : uptr { kSize = BV::kSize }; size()28*0b57cec5SDimitry Andric uptr size() const { return kSize; } 29*0b57cec5SDimitry Andric // No CTOR. clear()30*0b57cec5SDimitry Andric void clear() { 31*0b57cec5SDimitry Andric for (uptr i = 0; i < size(); i++) 32*0b57cec5SDimitry Andric v[i].clear(); 33*0b57cec5SDimitry Andric } 34*0b57cec5SDimitry Andric empty()35*0b57cec5SDimitry Andric bool empty() const { 36*0b57cec5SDimitry Andric for (uptr i = 0; i < size(); i++) 37*0b57cec5SDimitry Andric if (!v[i].empty()) 38*0b57cec5SDimitry Andric return false; 39*0b57cec5SDimitry Andric return true; 40*0b57cec5SDimitry Andric } 41*0b57cec5SDimitry Andric 42*0b57cec5SDimitry Andric // Returns true if a new edge was added. addEdge(uptr from,uptr to)43*0b57cec5SDimitry Andric bool addEdge(uptr from, uptr to) { 44*0b57cec5SDimitry Andric check(from, to); 45*0b57cec5SDimitry Andric return v[from].setBit(to); 46*0b57cec5SDimitry Andric } 47*0b57cec5SDimitry Andric 48*0b57cec5SDimitry Andric // Returns true if at least one new edge was added. addEdges(const BV & from,uptr to,uptr added_edges[],uptr max_added_edges)49*0b57cec5SDimitry Andric uptr addEdges(const BV &from, uptr to, uptr added_edges[], 50*0b57cec5SDimitry Andric uptr max_added_edges) { 51*0b57cec5SDimitry Andric uptr res = 0; 52*0b57cec5SDimitry Andric t1.copyFrom(from); 53*0b57cec5SDimitry Andric while (!t1.empty()) { 54*0b57cec5SDimitry Andric uptr node = t1.getAndClearFirstOne(); 55*0b57cec5SDimitry Andric if (v[node].setBit(to)) 56*0b57cec5SDimitry Andric if (res < max_added_edges) 57*0b57cec5SDimitry Andric added_edges[res++] = node; 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric return res; 60*0b57cec5SDimitry Andric } 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric // *EXPERIMENTAL* 63*0b57cec5SDimitry Andric // Returns true if an edge from=>to exist. 64*0b57cec5SDimitry Andric // This function does not use any global state except for 'this' itself, 65*0b57cec5SDimitry Andric // and thus can be called from different threads w/o locking. 66*0b57cec5SDimitry Andric // This would be racy. 67*0b57cec5SDimitry Andric // FIXME: investigate how much we can prove about this race being "benign". hasEdge(uptr from,uptr to)68*0b57cec5SDimitry Andric bool hasEdge(uptr from, uptr to) { return v[from].getBit(to); } 69*0b57cec5SDimitry Andric 70*0b57cec5SDimitry Andric // Returns true if the edge from=>to was removed. removeEdge(uptr from,uptr to)71*0b57cec5SDimitry Andric bool removeEdge(uptr from, uptr to) { 72*0b57cec5SDimitry Andric return v[from].clearBit(to); 73*0b57cec5SDimitry Andric } 74*0b57cec5SDimitry Andric 75*0b57cec5SDimitry Andric // Returns true if at least one edge *=>to was removed. removeEdgesTo(const BV & to)76*0b57cec5SDimitry Andric bool removeEdgesTo(const BV &to) { 77*0b57cec5SDimitry Andric bool res = 0; 78*0b57cec5SDimitry Andric for (uptr from = 0; from < size(); from++) { 79*0b57cec5SDimitry Andric if (v[from].setDifference(to)) 80*0b57cec5SDimitry Andric res = true; 81*0b57cec5SDimitry Andric } 82*0b57cec5SDimitry Andric return res; 83*0b57cec5SDimitry Andric } 84*0b57cec5SDimitry Andric 85*0b57cec5SDimitry Andric // Returns true if at least one edge from=>* was removed. removeEdgesFrom(const BV & from)86*0b57cec5SDimitry Andric bool removeEdgesFrom(const BV &from) { 87*0b57cec5SDimitry Andric bool res = false; 88*0b57cec5SDimitry Andric t1.copyFrom(from); 89*0b57cec5SDimitry Andric while (!t1.empty()) { 90*0b57cec5SDimitry Andric uptr idx = t1.getAndClearFirstOne(); 91*0b57cec5SDimitry Andric if (!v[idx].empty()) { 92*0b57cec5SDimitry Andric v[idx].clear(); 93*0b57cec5SDimitry Andric res = true; 94*0b57cec5SDimitry Andric } 95*0b57cec5SDimitry Andric } 96*0b57cec5SDimitry Andric return res; 97*0b57cec5SDimitry Andric } 98*0b57cec5SDimitry Andric removeEdgesFrom(uptr from)99*0b57cec5SDimitry Andric void removeEdgesFrom(uptr from) { 100*0b57cec5SDimitry Andric return v[from].clear(); 101*0b57cec5SDimitry Andric } 102*0b57cec5SDimitry Andric hasEdge(uptr from,uptr to)103*0b57cec5SDimitry Andric bool hasEdge(uptr from, uptr to) const { 104*0b57cec5SDimitry Andric check(from, to); 105*0b57cec5SDimitry Andric return v[from].getBit(to); 106*0b57cec5SDimitry Andric } 107*0b57cec5SDimitry Andric 108*0b57cec5SDimitry Andric // Returns true if there is a path from the node 'from' 109*0b57cec5SDimitry Andric // to any of the nodes in 'targets'. isReachable(uptr from,const BV & targets)110*0b57cec5SDimitry Andric bool isReachable(uptr from, const BV &targets) { 111*0b57cec5SDimitry Andric BV &to_visit = t1, 112*0b57cec5SDimitry Andric &visited = t2; 113*0b57cec5SDimitry Andric to_visit.copyFrom(v[from]); 114*0b57cec5SDimitry Andric visited.clear(); 115*0b57cec5SDimitry Andric visited.setBit(from); 116*0b57cec5SDimitry Andric while (!to_visit.empty()) { 117*0b57cec5SDimitry Andric uptr idx = to_visit.getAndClearFirstOne(); 118*0b57cec5SDimitry Andric if (visited.setBit(idx)) 119*0b57cec5SDimitry Andric to_visit.setUnion(v[idx]); 120*0b57cec5SDimitry Andric } 121*0b57cec5SDimitry Andric return targets.intersectsWith(visited); 122*0b57cec5SDimitry Andric } 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric // Finds a path from 'from' to one of the nodes in 'target', 125*0b57cec5SDimitry Andric // stores up to 'path_size' items of the path into 'path', 126*0b57cec5SDimitry Andric // returns the path length, or 0 if there is no path of size 'path_size'. findPath(uptr from,const BV & targets,uptr * path,uptr path_size)127*0b57cec5SDimitry Andric uptr findPath(uptr from, const BV &targets, uptr *path, uptr path_size) { 128*0b57cec5SDimitry Andric if (path_size == 0) 129*0b57cec5SDimitry Andric return 0; 130*0b57cec5SDimitry Andric path[0] = from; 131*0b57cec5SDimitry Andric if (targets.getBit(from)) 132*0b57cec5SDimitry Andric return 1; 133*0b57cec5SDimitry Andric // The function is recursive, so we don't want to create BV on stack. 134*0b57cec5SDimitry Andric // Instead of a getAndClearFirstOne loop we use the slower iterator. 135*0b57cec5SDimitry Andric for (typename BV::Iterator it(v[from]); it.hasNext(); ) { 136*0b57cec5SDimitry Andric uptr idx = it.next(); 137*0b57cec5SDimitry Andric if (uptr res = findPath(idx, targets, path + 1, path_size - 1)) 138*0b57cec5SDimitry Andric return res + 1; 139*0b57cec5SDimitry Andric } 140*0b57cec5SDimitry Andric return 0; 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric 143*0b57cec5SDimitry Andric // Same as findPath, but finds a shortest path. findShortestPath(uptr from,const BV & targets,uptr * path,uptr path_size)144*0b57cec5SDimitry Andric uptr findShortestPath(uptr from, const BV &targets, uptr *path, 145*0b57cec5SDimitry Andric uptr path_size) { 146*0b57cec5SDimitry Andric for (uptr p = 1; p <= path_size; p++) 147*0b57cec5SDimitry Andric if (findPath(from, targets, path, p) == p) 148*0b57cec5SDimitry Andric return p; 149*0b57cec5SDimitry Andric return 0; 150*0b57cec5SDimitry Andric } 151*0b57cec5SDimitry Andric 152*0b57cec5SDimitry Andric private: check(uptr idx1,uptr idx2)153*0b57cec5SDimitry Andric void check(uptr idx1, uptr idx2) const { 154*0b57cec5SDimitry Andric CHECK_LT(idx1, size()); 155*0b57cec5SDimitry Andric CHECK_LT(idx2, size()); 156*0b57cec5SDimitry Andric } 157*0b57cec5SDimitry Andric BV v[kSize]; 158*0b57cec5SDimitry Andric // Keep temporary vectors here since we can not create large objects on stack. 159*0b57cec5SDimitry Andric BV t1, t2; 160*0b57cec5SDimitry Andric }; 161*0b57cec5SDimitry Andric 162*0b57cec5SDimitry Andric } // namespace __sanitizer 163*0b57cec5SDimitry Andric 164*0b57cec5SDimitry Andric #endif // SANITIZER_BVGRAPH_H 165