xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_bitvector.h (revision 2e3f49888ec8851bafb22011533217487764fdb0)
1 //===-- sanitizer_bitvector.h -----------------------------------*- 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 // Specializer BitVector implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef SANITIZER_BITVECTOR_H
14 #define SANITIZER_BITVECTOR_H
15 
16 #include "sanitizer_common.h"
17 
18 namespace __sanitizer {
19 
20 // Fixed size bit vector based on a single basic integer.
21 template <class basic_int_t = uptr>
22 class BasicBitVector {
23  public:
24   enum SizeEnum : uptr { kSize = sizeof(basic_int_t) * 8 };
25 
26   uptr size() const { return kSize; }
27   // No CTOR.
28   void clear() { bits_ = 0; }
29   void setAll() { bits_ = ~(basic_int_t)0; }
30   bool empty() const { return bits_ == 0; }
31 
32   // Returns true if the bit has changed from 0 to 1.
33   bool setBit(uptr idx) {
34     basic_int_t old = bits_;
35     bits_ |= mask(idx);
36     return bits_ != old;
37   }
38 
39   // Returns true if the bit has changed from 1 to 0.
40   bool clearBit(uptr idx) {
41     basic_int_t old = bits_;
42     bits_ &= ~mask(idx);
43     return bits_ != old;
44   }
45 
46   bool getBit(uptr idx) const { return (bits_ & mask(idx)) != 0; }
47 
48   uptr getAndClearFirstOne() {
49     CHECK(!empty());
50     uptr idx = LeastSignificantSetBitIndex(bits_);
51     clearBit(idx);
52     return idx;
53   }
54 
55   // Do "this |= v" and return whether new bits have been added.
56   bool setUnion(const BasicBitVector &v) {
57     basic_int_t old = bits_;
58     bits_ |= v.bits_;
59     return bits_ != old;
60   }
61 
62   // Do "this &= v" and return whether any bits have been removed.
63   bool setIntersection(const BasicBitVector &v) {
64     basic_int_t old = bits_;
65     bits_ &= v.bits_;
66     return bits_ != old;
67   }
68 
69   // Do "this &= ~v" and return whether any bits have been removed.
70   bool setDifference(const BasicBitVector &v) {
71     basic_int_t old = bits_;
72     bits_ &= ~v.bits_;
73     return bits_ != old;
74   }
75 
76   void copyFrom(const BasicBitVector &v) { bits_ = v.bits_; }
77 
78   // Returns true if 'this' intersects with 'v'.
79   bool intersectsWith(const BasicBitVector &v) const {
80     return (bits_ & v.bits_) != 0;
81   }
82 
83   // for (BasicBitVector<>::Iterator it(bv); it.hasNext();) {
84   //   uptr idx = it.next();
85   //   use(idx);
86   // }
87   class Iterator {
88    public:
89     Iterator() { }
90     explicit Iterator(const BasicBitVector &bv) : bv_(bv) {}
91     bool hasNext() const { return !bv_.empty(); }
92     uptr next() { return bv_.getAndClearFirstOne(); }
93     void clear() { bv_.clear(); }
94    private:
95     BasicBitVector bv_;
96   };
97 
98  private:
99   basic_int_t mask(uptr idx) const {
100     CHECK_LT(idx, size());
101     return (basic_int_t)1UL << idx;
102   }
103   basic_int_t bits_;
104 };
105 
106 // Fixed size bit vector of (kLevel1Size*BV::kSize**2) bits.
107 // The implementation is optimized for better performance on
108 // sparse bit vectors, i.e. the those with few set bits.
109 template <uptr kLevel1Size = 1, class BV = BasicBitVector<> >
110 class TwoLevelBitVector {
111   // This is essentially a 2-level bit vector.
112   // Set bit in the first level BV indicates that there are set bits
113   // in the corresponding BV of the second level.
114   // This structure allows O(kLevel1Size) time for clear() and empty(),
115   // as well fast handling of sparse BVs.
116  public:
117   enum SizeEnum : uptr { kSize = BV::kSize * BV::kSize * kLevel1Size };
118   // No CTOR.
119 
120   uptr size() const { return kSize; }
121 
122   void clear() {
123     for (uptr i = 0; i < kLevel1Size; i++)
124       l1_[i].clear();
125   }
126 
127   void setAll() {
128     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
129       l1_[i0].setAll();
130       for (uptr i1 = 0; i1 < BV::kSize; i1++)
131         l2_[i0][i1].setAll();
132     }
133   }
134 
135   bool empty() const {
136     for (uptr i = 0; i < kLevel1Size; i++)
137       if (!l1_[i].empty())
138         return false;
139     return true;
140   }
141 
142   // Returns true if the bit has changed from 0 to 1.
143   bool setBit(uptr idx) {
144     check(idx);
145     uptr i0 = idx0(idx);
146     uptr i1 = idx1(idx);
147     uptr i2 = idx2(idx);
148     if (!l1_[i0].getBit(i1)) {
149       l1_[i0].setBit(i1);
150       l2_[i0][i1].clear();
151     }
152     bool res = l2_[i0][i1].setBit(i2);
153     // Printf("%s: %zd => %zd %zd %zd; %d\n", __func__,
154     // idx, i0, i1, i2, res);
155     return res;
156   }
157 
158   bool clearBit(uptr idx) {
159     check(idx);
160     uptr i0 = idx0(idx);
161     uptr i1 = idx1(idx);
162     uptr i2 = idx2(idx);
163     bool res = false;
164     if (l1_[i0].getBit(i1)) {
165       res = l2_[i0][i1].clearBit(i2);
166       if (l2_[i0][i1].empty())
167         l1_[i0].clearBit(i1);
168     }
169     return res;
170   }
171 
172   bool getBit(uptr idx) const {
173     check(idx);
174     uptr i0 = idx0(idx);
175     uptr i1 = idx1(idx);
176     uptr i2 = idx2(idx);
177     // Printf("%s: %zd => %zd %zd %zd\n", __func__, idx, i0, i1, i2);
178     return l1_[i0].getBit(i1) && l2_[i0][i1].getBit(i2);
179   }
180 
181   uptr getAndClearFirstOne() {
182     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
183       if (l1_[i0].empty()) continue;
184       uptr i1 = l1_[i0].getAndClearFirstOne();
185       uptr i2 = l2_[i0][i1].getAndClearFirstOne();
186       if (!l2_[i0][i1].empty())
187         l1_[i0].setBit(i1);
188       uptr res = i0 * BV::kSize * BV::kSize + i1 * BV::kSize + i2;
189       // Printf("getAndClearFirstOne: %zd %zd %zd => %zd\n", i0, i1, i2, res);
190       return res;
191     }
192     CHECK(0);
193     return 0;
194   }
195 
196   // Do "this |= v" and return whether new bits have been added.
197   bool setUnion(const TwoLevelBitVector &v) {
198     bool res = false;
199     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
200       BV t = v.l1_[i0];
201       while (!t.empty()) {
202         uptr i1 = t.getAndClearFirstOne();
203         if (l1_[i0].setBit(i1))
204           l2_[i0][i1].clear();
205         if (l2_[i0][i1].setUnion(v.l2_[i0][i1]))
206           res = true;
207       }
208     }
209     return res;
210   }
211 
212   // Do "this &= v" and return whether any bits have been removed.
213   bool setIntersection(const TwoLevelBitVector &v) {
214     bool res = false;
215     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
216       if (l1_[i0].setIntersection(v.l1_[i0]))
217         res = true;
218       if (!l1_[i0].empty()) {
219         BV t = l1_[i0];
220         while (!t.empty()) {
221           uptr i1 = t.getAndClearFirstOne();
222           if (l2_[i0][i1].setIntersection(v.l2_[i0][i1]))
223             res = true;
224           if (l2_[i0][i1].empty())
225             l1_[i0].clearBit(i1);
226         }
227       }
228     }
229     return res;
230   }
231 
232   // Do "this &= ~v" and return whether any bits have been removed.
233   bool setDifference(const TwoLevelBitVector &v) {
234     bool res = false;
235     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
236       BV t = l1_[i0];
237       t.setIntersection(v.l1_[i0]);
238       while (!t.empty()) {
239         uptr i1 = t.getAndClearFirstOne();
240         if (l2_[i0][i1].setDifference(v.l2_[i0][i1]))
241           res = true;
242         if (l2_[i0][i1].empty())
243           l1_[i0].clearBit(i1);
244       }
245     }
246     return res;
247   }
248 
249   void copyFrom(const TwoLevelBitVector &v) {
250     clear();
251     setUnion(v);
252   }
253 
254   // Returns true if 'this' intersects with 'v'.
255   bool intersectsWith(const TwoLevelBitVector &v) const {
256     for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
257       BV t = l1_[i0];
258       t.setIntersection(v.l1_[i0]);
259       while (!t.empty()) {
260         uptr i1 = t.getAndClearFirstOne();
261         if (!v.l1_[i0].getBit(i1)) continue;
262         if (l2_[i0][i1].intersectsWith(v.l2_[i0][i1]))
263           return true;
264       }
265     }
266     return false;
267   }
268 
269   // for (TwoLevelBitVector<>::Iterator it(bv); it.hasNext();) {
270   //   uptr idx = it.next();
271   //   use(idx);
272   // }
273   class Iterator {
274    public:
275     Iterator() { }
276     explicit Iterator(const TwoLevelBitVector &bv) : bv_(bv), i0_(0), i1_(0) {
277       it1_.clear();
278       it2_.clear();
279     }
280 
281     bool hasNext() const {
282       if (it1_.hasNext()) return true;
283       for (uptr i = i0_; i < kLevel1Size; i++)
284         if (!bv_.l1_[i].empty()) return true;
285       return false;
286     }
287 
288     uptr next() {
289       // Printf("++++: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
290       //       it2_.hasNext(), kSize);
291       if (!it1_.hasNext() && !it2_.hasNext()) {
292         for (; i0_ < kLevel1Size; i0_++) {
293           if (bv_.l1_[i0_].empty()) continue;
294           it1_ = typename BV::Iterator(bv_.l1_[i0_]);
295           // Printf("+i0: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
296           //   it2_.hasNext(), kSize);
297           break;
298         }
299       }
300       if (!it2_.hasNext()) {
301         CHECK(it1_.hasNext());
302         i1_ = it1_.next();
303         it2_ = typename BV::Iterator(bv_.l2_[i0_][i1_]);
304         // Printf("++i1: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
305         //       it2_.hasNext(), kSize);
306       }
307       CHECK(it2_.hasNext());
308       uptr i2 = it2_.next();
309       uptr res = i0_ * BV::kSize * BV::kSize + i1_ * BV::kSize + i2;
310       // Printf("+ret: %zd %zd; %d %d; size %zd; res: %zd\n", i0_, i1_,
311       //       it1_.hasNext(), it2_.hasNext(), kSize, res);
312       if (!it1_.hasNext() && !it2_.hasNext())
313         i0_++;
314       return res;
315     }
316 
317    private:
318     const TwoLevelBitVector &bv_;
319     uptr i0_, i1_;
320     typename BV::Iterator it1_, it2_;
321   };
322 
323  private:
324   void check(uptr idx) const { CHECK_LE(idx, size()); }
325 
326   uptr idx0(uptr idx) const {
327     uptr res = idx / (BV::kSize * BV::kSize);
328     CHECK_LE(res, kLevel1Size);
329     return res;
330   }
331 
332   uptr idx1(uptr idx) const {
333     uptr res = (idx / BV::kSize) % BV::kSize;
334     CHECK_LE(res, BV::kSize);
335     return res;
336   }
337 
338   uptr idx2(uptr idx) const {
339     uptr res = idx % BV::kSize;
340     CHECK_LE(res, BV::kSize);
341     return res;
342   }
343 
344   BV l1_[kLevel1Size];
345   BV l2_[kLevel1Size][BV::kSize];
346 };
347 
348 } // namespace __sanitizer
349 
350 #endif // SANITIZER_BITVECTOR_H
351