v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
sparse-bit-vector.h
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_UTILS_SPARSE_BIT_VECTOR_H_
6#define V8_UTILS_SPARSE_BIT_VECTOR_H_
7
8#include "src/base/bits.h"
9#include "src/base/iterator.h"
10#include "src/zone/zone.h"
11
12namespace v8 {
13namespace internal {
14
15// A sparse bit vector implementation optimized for small sizes.
16// For up to {kNumBitsPerSegment} bits, no additional storage is needed, and
17// accesses should be nearly as fast as {BitVector}.
19 // 6 words for the bits plus {offset} plus {next} will be 8 machine words per
20 // {Segment}. Most bit vectors are expected to fit in that single {Segment}.
21 static constexpr int kNumWordsPerSegment = 6;
24
25 struct Segment {
26 // Offset of the first bit in this segment.
27 int offset = 0;
28 // {words} covers bits [{offset}, {offset + kNumBitsPerSegment}).
29 uintptr_t words[kNumWordsPerSegment] = {0};
30 // The next segment (with strict larger offset), or {nullptr}.
31 Segment* next = nullptr;
32
33 bool empty() const {
34 return std::all_of(std::begin(words), std::end(words),
35 [](auto segment) { return segment == 0; });
36 }
37 };
38
39 // Check that {Segment}s are nicely aligned, for (hopefully) better cache line
40 // alignment.
41 static_assert(sizeof(Segment) == 8 * kSystemPointerSize);
42
43 public:
44 // An iterator to iterate all set bits.
45 class Iterator : public base::iterator<std::forward_iterator_tag, int> {
46 public:
47 struct EndTag {};
48
49 explicit Iterator(EndTag) {}
50 explicit Iterator(const Segment* segment) {
51 // {segment} is {&first_segment_}, so cannot be null initially.
52 do {
53 for (int word = 0; word < kNumWordsPerSegment; ++word) {
54 if (segment->words[word] == 0) continue;
55 int bit_in_word =
57 segment_ = segment;
58 bit_in_segment_ = word * kBitsPerWord + bit_in_word;
59 return;
60 }
61 segment = segment->next;
62 } while (segment != nullptr);
66 }
67
68 int operator*() const {
70 int offset = segment_->offset + bit_in_segment_;
72 return offset;
73 }
74
75 bool operator==(const Iterator& other) const {
76 return segment_ == other.segment_ &&
77 bit_in_segment_ == other.bit_in_segment_;
78 }
79
80 bool operator!=(const Iterator& other) const { return !(*this == other); }
81
82 void operator++() {
83 int word = bit_in_segment_ / kBitsPerWord;
84 int bit_in_word = bit_in_segment_ % kBitsPerWord;
85 if (bit_in_word < kBitsPerWord - 1) {
86 uintptr_t remaining_bits =
87 segment_->words[word] &
88 (std::numeric_limits<uintptr_t>::max() << (1 + bit_in_word));
89 if (remaining_bits) {
90 int next_bit_in_word = base::bits::CountTrailingZeros(remaining_bits);
91 DCHECK_LT(bit_in_word, next_bit_in_word);
92 bit_in_segment_ = word * kBitsPerWord + next_bit_in_word;
93 return;
94 }
95 }
96 // No remaining bits in the current word. Search in succeeding words and
97 // segments.
98 ++word;
99 do {
100 for (; word < kNumWordsPerSegment; ++word) {
101 if (segment_->words[word] == 0) continue;
102 bit_in_word = base::bits::CountTrailingZeros(segment_->words[word]);
103 bit_in_segment_ = word * kBitsPerWord + bit_in_word;
104 return;
105 }
106 segment_ = segment_->next;
107 word = 0;
108 } while (segment_);
109 // If we reach here, there are no more bits.
110 bit_in_segment_ = 0;
111 DCHECK_EQ(*this, Iterator{EndTag{}});
112 }
113
114 private:
115 const Segment* segment_ = nullptr;
117 };
118
120
121 explicit SparseBitVector(Zone* zone) : zone_(zone) {}
122
123 V8_INLINE bool Contains(int i) const {
124 DCHECK_LE(0, i);
125 const Segment* segment = &first_segment_;
126 // Explicit fast path for the first segment which always starts at offset 0.
128 do {
129 segment = segment->next;
130 if (!segment) return false;
131 } while (segment->offset <= i - kNumBitsPerSegment);
132 if (segment->offset > i) return false;
133 }
134 return contains(segment, i);
135 }
136
137 V8_INLINE void Add(int i) {
138 DCHECK_LE(0, i);
139 Segment* last = nullptr;
140 Segment* segment = &first_segment_;
141 // Explicit fast path for the first segment which always starts at offset 0.
143 do {
144 last = segment;
145 segment = segment->next;
146 if (V8_UNLIKELY(!segment)) return InsertBitAfter(last, i);
147 } while (segment->offset <= i - kNumBitsPerSegment);
148 if (V8_UNLIKELY(segment->offset > i)) return InsertBitAfter(last, i);
149 }
150 set(segment, i);
151 }
152
153 V8_INLINE void Remove(int i) {
154 DCHECK_LE(0, i);
155 Segment* segment = &first_segment_;
156 // Explicit fast path for the first segment which always starts at offset 0.
158 do {
159 segment = segment->next;
160 if (V8_UNLIKELY(!segment)) return;
161 } while (segment->offset <= i - kNumBitsPerSegment);
162 if (V8_UNLIKELY(segment->offset > i)) return;
163 }
164 unset(segment, i);
165 }
166
167 void Union(const SparseBitVector& other) {
168 // Always remember the segment before {segment}, because we sometimes need
169 // to insert *before* {segment}, but we have to backlinks.
170 Segment* last = nullptr;
171 Segment* segment = &first_segment_;
172 // Iterate all segments in {other}, merging with with existing segments in
173 // {this}, or inserting new segments when needed.
174 for (const Segment* other_segment = &other.first_segment_; other_segment;
175 other_segment = other_segment->next) {
176 // Find the first segment whose offset is >= {other_segment}'s offset.
177 while (segment && segment->offset < other_segment->offset) {
178 last = segment;
179 segment = segment->next;
180 }
181 // Now either merge {other_segment} into {segment}, or insert between
182 // {last} and {segment}.
183 if (segment && segment->offset == other_segment->offset) {
184 std::transform(std::begin(segment->words), std::end(segment->words),
185 std::begin(other_segment->words),
186 std::begin(segment->words), std::bit_or<uintptr_t>{});
187 } else if (!other_segment->empty()) {
188 DCHECK_LT(last->offset, other_segment->offset);
189 Segment* new_segment = zone_->New<Segment>();
190 new_segment->offset = other_segment->offset;
191 std::copy(std::begin(other_segment->words),
192 std::end(other_segment->words),
193 std::begin(new_segment->words));
194 InsertSegmentAfter(last, new_segment);
195 last = new_segment;
196 }
197 }
198 }
199
200 Iterator begin() const { return Iterator{&first_segment_}; }
201 Iterator end() const { return Iterator{Iterator::EndTag{}}; }
202
203 private:
204 static std::pair<int, int> GetWordAndBitInWord(const Segment* segment,
205 int i) {
206 DCHECK_LE(segment->offset, i);
208 int bit_in_segment = i - segment->offset;
209 return {bit_in_segment / kBitsPerWord, bit_in_segment % kBitsPerWord};
210 }
211
212 V8_NOINLINE void InsertBitAfter(Segment* segment, int i) {
213 Segment* new_segment = zone_->New<Segment>();
215 set(new_segment, i);
216 InsertSegmentAfter(segment, new_segment);
217 DCHECK(Contains(i));
218 }
219
220 V8_NOINLINE void InsertSegmentAfter(Segment* segment, Segment* new_segment) {
221 DCHECK_NOT_NULL(segment);
222 DCHECK_LT(segment->offset, new_segment->offset);
223 insert_after(segment, new_segment);
224 DCHECK(CheckConsistency(segment));
225 DCHECK(CheckConsistency(new_segment));
226 }
227
228 static bool contains(const Segment* segment, int i) {
229 auto [word, bit] = GetWordAndBitInWord(segment, i);
230 return (segment->words[word] >> bit) & 1;
231 }
232
233 static bool set(Segment* segment, int i) {
234 auto [word, bit] = GetWordAndBitInWord(segment, i);
235 return segment->words[word] |= uintptr_t{1} << bit;
236 }
237
238 static bool unset(Segment* segment, int i) {
239 auto [word, bit] = GetWordAndBitInWord(segment, i);
240 return segment->words[word] &= ~(uintptr_t{1} << bit);
241 }
242
243 static void insert_after(Segment* segment, Segment* new_next) {
244 DCHECK_NULL(new_next->next);
245 DCHECK_LT(segment->offset, new_next->offset);
246 new_next->next = segment->next;
247 segment->next = new_next;
248 }
249
251 if ((segment->offset % kNumBitsPerSegment) != 0) return false;
252 if (segment->next && segment->next->offset <= segment->offset) return false;
253 return true;
254 }
255
258};
259
260} // namespace internal
261} // namespace v8
262
263#endif // V8_UTILS_SPARSE_BIT_VECTOR_H_
bool operator!=(const Iterator &other) const
bool operator==(const Iterator &other) const
V8_NOINLINE void InsertBitAfter(Segment *segment, int i)
static void insert_after(Segment *segment, Segment *new_next)
V8_WARN_UNUSED_RESULT bool CheckConsistency(const Segment *segment)
void Union(const SparseBitVector &other)
static bool unset(Segment *segment, int i)
V8_INLINE bool Contains(int i) const
static bool set(Segment *segment, int i)
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(SparseBitVector)
static constexpr int kNumWordsPerSegment
static bool contains(const Segment *segment, int i)
static std::pair< int, int > GetWordAndBitInWord(const Segment *segment, int i)
V8_NOINLINE void InsertSegmentAfter(Segment *segment, Segment *new_segment)
static constexpr int kNumBitsPerSegment
static constexpr int kBitsPerWord
T * New(Args &&... args)
Definition zone.h:114
int32_t offset
constexpr unsigned CountTrailingZeros(T value)
Definition bits.h:144
constexpr int kBitsPerByte
Definition globals.h:682
constexpr int kSystemPointerSize
Definition globals.h:410
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
uintptr_t words[kNumWordsPerSegment]
#define V8_INLINE
Definition v8config.h:500
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
#define V8_UNLIKELY(condition)
Definition v8config.h:660
#define V8_NOINLINE
Definition v8config.h:586