xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_bvgraph.h (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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