• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef PANDA_RUNTIME_MEM_GC_BITMAP_H_
17 #define PANDA_RUNTIME_MEM_GC_BITMAP_H_
18 
19 // clash with mingw
20 #ifndef PANDA_TARGET_WINDOWS
21 #include <securec.h>
22 #endif
23 #include <cstddef>
24 #include <cstdint>
25 #include <cstring>
26 
27 #include "libpandabase/macros.h"
28 #include "libpandabase/mem/mem.h"
29 #include "libpandabase/utils/bit_utils.h"
30 #include "libpandabase/utils/math_helpers.h"
31 #include "libpandabase/utils/span.h"
32 
33 namespace panda::mem {
34 
35 /**
36  * Abstract base class. Constructor/destructor are protected. No virtual function to avoid dynamic polymorphism.
37  */
38 class Bitmap {
39 public:
40     using BitmapWordType = uintptr_t;
41 
Size()42     size_t Size() const
43     {
44         return bitsize_;
45     }
46 
ClearAllBits()47     void ClearAllBits()
48     {
49 #ifndef PANDA_TARGET_WINDOWS
50         (void)memset_s(bitmap_.Data(), bitmap_.SizeBytes(), 0, bitmap_.SizeBytes());
51 #endif
52     }
53 
GetBitMap()54     Span<BitmapWordType> GetBitMap()
55     {
56         return bitmap_;
57     }
58 
59     static const size_t BITSPERBYTE = 8;
60     static const size_t BITSPERWORD = BITSPERBYTE * sizeof(BitmapWordType);
61     static constexpr size_t LOG_BITSPERBYTE = panda::helpers::math::GetIntLog2(static_cast<uint64_t>(BITSPERBYTE));
62     static constexpr size_t LOG_BITSPERWORD = panda::helpers::math::GetIntLog2(static_cast<uint64_t>(BITSPERWORD));
63 
64 protected:
65     /**
66      * \brief Set the bit indexed by bit_offset.
67      * @param bit_offset - index of the bit to set.
68      */
SetBit(size_t bit_offset)69     void SetBit(size_t bit_offset)
70     {
71         CheckBitOffset(bit_offset);
72         bitmap_[GetWordIdx(bit_offset)] |= GetBitMask(bit_offset);
73     }
74 
75     /**
76      * \brief Clear the bit indexed by bit_offset.
77      * @param bit_offset - index of the bit to clear.
78      */
ClearBit(size_t bit_offset)79     void ClearBit(size_t bit_offset)
80     {
81         CheckBitOffset(bit_offset);
82         bitmap_[GetWordIdx(bit_offset)] &= ~GetBitMask(bit_offset);
83     }
84 
85     /**
86      * \brief Test the bit indexed by bit_offset.
87      * @param bit_offset - index of the bit to test.
88      * @return Returns value of indexed bit.
89      */
TestBit(size_t bit_offset)90     bool TestBit(size_t bit_offset) const
91     {
92         CheckBitOffset(bit_offset);
93         return (bitmap_[GetWordIdx(bit_offset)] & GetBitMask(bit_offset)) != 0;
94     }
95 
96     /**
97      * \brief Atomically set bit indexed by bit_offset. If the bit is not set, set it atomically. Otherwise, do nothing.
98      * @param bit_offset - index of the bit to set.
99      * @return Returns old value of the bit.
100      */
101     bool AtomicTestAndSetBit(size_t bit_offset);
102 
103     /**
104      * \brief Atomically clear bit corresponding to addr. If the bit is set, clear it atomically. Otherwise, do nothing.
105      * @param addr - addr must be aligned to BYTESPERCHUNK.
106      * @return Returns old value of the bit.
107      */
108     bool AtomicTestAndClearBit(size_t bit_offset);
109 
110     /**
111      * \brief Atomically test bit corresponding to addr.
112      * @param addr - addr must be aligned to BYTESPERCHUNK.
113      * @return Returns the value of the bit.
114      */
115     bool AtomicTestBit(size_t bit_offset);
116 
117     /**
118      * \brief Iterate over marked bits of bitmap sequentially.
119      * Finish iteration if the visitor returns false.
120      * @tparam VisitorType
121      * @param visitor - function pointer or functor.
122      */
123     template <typename VisitorType>
IterateOverSetBits(const VisitorType & visitor)124     void IterateOverSetBits(const VisitorType &visitor)
125     {
126         IterateOverSetBitsInRange(0, Size(), visitor);
127     }
128 
129     /**
130      * \brief Iterate over all bits of bitmap sequentially.
131      * @tparam VisitorType
132      * @param visitor - function pointer or functor.
133      */
134     template <typename VisitorType>
IterateOverBits(const VisitorType & visitor)135     void IterateOverBits(const VisitorType &visitor)
136     {
137         IterateOverBitsInRange(0, Size(), visitor);
138     }
139 
140     /**
141      * \brief Iterate over marked bits in range [begin, end) sequentially.
142      * Finish iteration if the visitor returns false.
143      * @tparam VisitorType
144      * @param begin - beginning index of the range, inclusive.
145      * @param end - end index of the range, exclusive.
146      * @param visitor - function pointer or functor.
147      */
148     template <typename VisitorType>
IterateOverSetBitsInRange(size_t begin,size_t end,const VisitorType & visitor)149     void IterateOverSetBitsInRange(size_t begin, size_t end, const VisitorType &visitor)
150     {
151         CheckBitRange(begin, end);
152         if (UNLIKELY(begin == end)) {
153             return;
154         }
155 
156         // first word, clear bits before begin
157         auto bitmap_word = bitmap_[GetWordIdx(begin)];
158         auto offset_within_word = GetBitIdxWithinWord(begin);
159         bitmap_word &= GetRangeBitMask(offset_within_word, BITSPERWORD);
160         auto offset_word_begin = GetWordIdx(begin) * BITSPERWORD;
161         const bool RIGHT_ALIGNED = (GetBitIdxWithinWord(end) == 0);
162         const auto OFFSET_LAST_WORD_BEGIN = GetWordIdx(end) * BITSPERWORD;
163 
164         do {
165             if (offset_word_begin == OFFSET_LAST_WORD_BEGIN && !RIGHT_ALIGNED) {
166                 // last partial word, clear bits after right boundary
167                 auto mask = GetRangeBitMask(0, GetBitIdxWithinWord(end));
168                 bitmap_word &= mask;
169             }
170             // loop over bits of bitmap_word
171             while (offset_within_word < BITSPERWORD) {
172                 if (bitmap_word == 0) {
173                     break;
174                 }
175                 offset_within_word = Ctz(bitmap_word);
176                 if (!visitor(offset_word_begin + offset_within_word)) {
177                     return;
178                 }
179                 bitmap_word &= ~GetBitMask(offset_within_word);
180             }
181 
182             offset_word_begin += BITSPERWORD;
183             if (offset_word_begin < end) {
184                 bitmap_word = bitmap_[GetWordIdx(offset_word_begin)];
185                 offset_within_word = 0;
186             }
187         } while (offset_word_begin < end);
188     }
189 
190     /**
191      * \brief Iterate over all bits in range [begin, end) sequentially.
192      * @tparam VisitorType
193      * @param begin - beginning index of the range, inclusive.
194      * @param end - end index of the range, exclusive.
195      * @param visitor - function pointer or functor.
196      */
197     template <typename VisitorType>
IterateOverBitsInRange(size_t begin,size_t end,const VisitorType & visitor)198     void IterateOverBitsInRange(size_t begin, size_t end, const VisitorType &visitor)
199     {
200         CheckBitRange(begin, end);
201         for (size_t i = begin; i < end; ++i) {
202             visitor(i);
203         }
204     }
205 
206     /**
207      * \brief Clear all bits in range [begin, end).
208      * @param begin - beginning index of the range, inclusive.
209      * @param end - end index of the range, exclusive.
210      */
211     void ClearBitsInRange(size_t begin, size_t end);
212 
213     /**
214      * \brief Set all bits in range [begin, end). [begin, end) must be within a BitmapWord.
215      * @param begin - beginning index of the range, inclusive.
216      * @param end - end index of the range, exclusive.
217      */
SetRangeWithinWord(size_t begin,size_t end)218     void SetRangeWithinWord(size_t begin, size_t end)
219     {
220         if (LIKELY(begin != end)) {
221             ModifyRangeWithinWord<true>(begin, end);
222         }
223     }
224 
225     /**
226      * \brief Clear all bits in range [begin, end). [begin, end) must be within a BitmapWord.
227      * @param begin - beginning index of the range, inclusive.
228      * @param end - end index of the range, exclusive.
229      */
ClearRangeWithinWord(size_t begin,size_t end)230     void ClearRangeWithinWord(size_t begin, size_t end)
231     {
232         if (LIKELY(begin != end)) {
233             ModifyRangeWithinWord<false>(begin, end);
234         }
235     }
236 
237     /**
238      * \brief Set all BitmapWords in index range [begin, end).
239      * @param begin - beginning BitmapWord index of the range, inclusive.
240      * @param end - end BitmapWord index of the range, exclusive.
241      */
SetWords(size_t word_begin,size_t word_end)242     void SetWords([[maybe_unused]] size_t word_begin, [[maybe_unused]] size_t word_end)
243     {
244         ASSERT(word_begin <= word_end);
245 #ifndef PANDA_TARGET_WINDOWS
246         if (UNLIKELY(word_begin == word_end)) {
247             return;
248         }
249         (void)memset_s(&bitmap_[word_begin], (word_end - word_begin) * sizeof(BitmapWordType),
250                        ~static_cast<unsigned char>(0), (word_end - word_begin) * sizeof(BitmapWordType));
251 #endif
252     }
253 
254     /**
255      * \brief Clear all BitmapWords in index range [begin, end).
256      * @param begin - beginning BitmapWord index of the range, inclusive.
257      * @param end - end BitmapWord index of the range, exclusive.
258      */
ClearWords(size_t word_begin,size_t word_end)259     void ClearWords([[maybe_unused]] size_t word_begin, [[maybe_unused]] size_t word_end)
260     {
261         ASSERT(word_begin <= word_end);
262 #ifndef PANDA_TARGET_WINDOWS
263         if (UNLIKELY(word_begin == word_end)) {
264             return;
265         }
266         (void)memset_s(&bitmap_[word_begin], (word_end - word_begin) * sizeof(BitmapWordType),
267                        static_cast<unsigned char>(0), (word_end - word_begin) * sizeof(BitmapWordType));
268 #endif
269     }
270 
Bitmap(BitmapWordType * bitmap,size_t bitsize)271     explicit Bitmap(BitmapWordType *bitmap, size_t bitsize)
272         : bitmap_(bitmap, (AlignUp(bitsize, BITSPERWORD) >> LOG_BITSPERWORD)), bitsize_(bitsize)
273     {
274     }
275     ~Bitmap() = default;
276     // do we need special copy/move constructor?
277     NO_COPY_SEMANTIC(Bitmap);
278     NO_MOVE_SEMANTIC(Bitmap);
279 
280 private:
281     Span<BitmapWordType> bitmap_;
282     size_t bitsize_ = 0;
283 
284     /**
285      * \brief Compute word index from bit index.
286      * @param bit_offset - bit index.
287      * @return Returns BitmapWord Index of bit_offset.
288      */
GetWordIdx(size_t bit_offset)289     static size_t GetWordIdx(size_t bit_offset)
290     {
291         return bit_offset >> LOG_BITSPERWORD;
292     }
293 
294     /**
295      * \brief Compute bit index within a BitmapWord from bit index.
296      * @param bit_offset - bit index.
297      * @return Returns bit index within a BitmapWord.
298      */
GetBitIdxWithinWord(size_t bit_offset)299     size_t GetBitIdxWithinWord(size_t bit_offset) const
300     {
301         CheckBitOffset(bit_offset);
302         constexpr auto BIT_INDEX_MASK = static_cast<size_t>((1UL << LOG_BITSPERWORD) - 1);
303         return bit_offset & BIT_INDEX_MASK;
304     }
305 
306     /**
307      * \brief Compute bit mask from bit index.
308      * @param bit_offset - bit index.
309      * @return Returns bit mask of bit_offset.
310      */
GetBitMask(size_t bit_offset)311     BitmapWordType GetBitMask(size_t bit_offset) const
312     {
313         return 1UL << GetBitIdxWithinWord(bit_offset);
314     }
315 
316     /**
317      * \brief Compute bit mask of range [begin_within_word, end_within_word).
318      * @param begin_within_word - beginning index within word, in range [0, BITSPERWORD).
319      * @param end_within_word - end index within word, in range [0, BITSPERWORD]. Make sure end_within_word is
320      * BITSPERWORD(instead of 0) if you want to cover from certain bit to last. [0, 0) is the only valid case when
321      * end_within_word is 0.
322      * @return Returns bit mask.
323      */
GetRangeBitMask(size_t begin_within_word,size_t end_within_word)324     BitmapWordType GetRangeBitMask(size_t begin_within_word, size_t end_within_word) const
325     {
326         ASSERT(begin_within_word < BITSPERWORD);
327         ASSERT(end_within_word <= BITSPERWORD);
328         ASSERT(begin_within_word <= end_within_word);
329         auto end_mask =
330             (end_within_word == BITSPERWORD) ? ~static_cast<BitmapWordType>(0) : GetBitMask(end_within_word) - 1;
331         return end_mask - (GetBitMask(begin_within_word) - 1);
332     }
333 
334     /**
335      * \brief Check if bit_offset is valid.
336      */
CheckBitOffset(size_t bit_offset)337     void CheckBitOffset([[maybe_unused]] size_t bit_offset) const
338     {
339         ASSERT(bit_offset <= Size());
340     }
341 
342     /**
343      * \brief According to SET, set or clear range [begin, end) within a BitmapWord.
344      * @param begin - beginning global bit index.
345      * @param end - end global bit index.
346      */
347     template <bool SET>
ModifyRangeWithinWord(size_t begin,size_t end)348     ALWAYS_INLINE void ModifyRangeWithinWord(size_t begin, size_t end)
349     {
350         CheckBitRange(begin, end);
351 
352         if (UNLIKELY(begin == end)) {
353             return;
354         }
355 
356         BitmapWordType mask;
357         if (end % BITSPERWORD == 0) {
358             ASSERT(GetWordIdx(end) - GetWordIdx(begin) == 1);
359             mask = GetRangeBitMask(GetBitIdxWithinWord(begin), BITSPERWORD);
360         } else {
361             ASSERT(GetWordIdx(end) == GetWordIdx(begin));
362             mask = GetRangeBitMask(GetBitIdxWithinWord(begin), GetBitIdxWithinWord(end));
363         }
364 
365         if (SET) {
366             bitmap_[GetWordIdx(begin)] |= mask;
367         } else {
368             bitmap_[GetWordIdx(begin)] &= ~mask;
369         }
370     }
371 
372     /**
373      * \brief Check if bit range [begin, end) is valid.
374      */
CheckBitRange(size_t begin,size_t end)375     void CheckBitRange([[maybe_unused]] size_t begin, [[maybe_unused]] size_t end) const
376     {
377         ASSERT(begin < Size());
378         ASSERT(end <= Size());
379         ASSERT(begin <= end);
380     }
381 };
382 
383 /**
384  * Memory bitmap, binding a continuous range of memory to a bitmap.
385  * One bit represents BYTESPERCHUNK bytes of memory.
386  */
387 template <size_t BYTESPERCHUNK = 1, typename pointer_type = object_pointer_type>
388 class MemBitmap : public Bitmap {
389 public:
MemBitmap(void * mem_addr,size_t heap_size,void * bitmap_addr)390     explicit MemBitmap(void *mem_addr, size_t heap_size, void *bitmap_addr)
391         : Bitmap(static_cast<BitmapWordType *>(bitmap_addr), heap_size / BYTESPERCHUNK),
392           begin_addr_(ToPointerType(mem_addr)),
393           end_addr_(begin_addr_ + heap_size)
394     {
395     }
396     NO_COPY_SEMANTIC(MemBitmap);
397     NO_MOVE_SEMANTIC(MemBitmap);
398 
399     /**
400      * \brief Reinitialize the MemBitmap for new memory range.
401      * The size of range will be the same as the initial
402      * because we reuse the same bitmap storage.
403      * @param mem_addr - start addr of the new range.
404      */
ReInitializeMemoryRange(void * mem_addr)405     void ReInitializeMemoryRange(void *mem_addr)
406     {
407         begin_addr_ = ToPointerType(mem_addr);
408         end_addr_ = begin_addr_ + MemSizeInBytes();
409         Bitmap::ClearAllBits();
410     }
411 
GetBitMapSizeInByte(size_t heap_size)412     inline static constexpr size_t GetBitMapSizeInByte(size_t heap_size)
413     {
414         ASSERT(heap_size % BYTESPERCHUNK == 0);
415         size_t bit_size = heap_size / BYTESPERCHUNK;
416         return (AlignUp(bit_size, BITSPERWORD) >> Bitmap::LOG_BITSPERWORD) * sizeof(BitmapWordType);
417     }
418 
419     ~MemBitmap() = default;
420 
MemSizeInBytes()421     size_t MemSizeInBytes() const
422     {
423         return Size() * BYTESPERCHUNK;
424     }
425 
GetHeapRange()426     inline std::pair<uintptr_t, uintptr_t> GetHeapRange()
427     {
428         return {begin_addr_, end_addr_};
429     }
430 
431     /**
432      * \brief Set bit corresponding to addr.
433      * @param addr - addr must be aligned to BYTESPERCHUNK.
434      */
Set(void * addr)435     void Set(void *addr)
436     {
437         CheckAddrValidity(addr);
438         SetBit(AddrToBitOffset(ToPointerType(addr)));
439     }
440 
441     /**
442      * \brief Clear bit corresponding to addr.
443      * @param addr - addr must be aligned to BYTESPERCHUNK.
444      */
Clear(void * addr)445     void Clear(void *addr)
446     {
447         CheckAddrValidity(addr);
448         ClearBit(AddrToBitOffset(ToPointerType(addr)));
449     }
450 
451     /**
452      * \brief Clear bits corresponding to addr range [begin, end).
453      */
ClearRange(void * begin,void * end)454     ALWAYS_INLINE void ClearRange(void *begin, void *end)
455     {
456         CheckHalfClosedHalfOpenAddressRange(begin, end);
457         ClearBitsInRange(AddrToBitOffset(ToPointerType(begin)), EndAddrToBitOffset(ToPointerType(end)));
458     }
459 
460     /**
461      * \brief Test bit corresponding to addr.
462      * @param addr - addr must be aligned to BYTESPERCHUNK.
463      */
Test(const void * addr)464     bool Test(const void *addr) const
465     {
466         CheckAddrValidity(addr);
467         return TestBit(AddrToBitOffset(ToPointerType(addr)));
468     }
469 
470     /**
471      * \brief Test bit corresponding to addr if addr is valid.
472      * @return value of indexed bit if addr is valid. If addr is invalid then false
473      */
TestIfAddrValid(const void * addr)474     bool TestIfAddrValid(const void *addr) const
475     {
476         if (IsAddrValid(addr)) {
477             return TestBit(AddrToBitOffset(ToPointerType(addr)));
478         }
479         return false;
480     }
481 
482     /**
483      * \brief Atomically set bit corresponding to addr. If the bit is not set, set it atomically. Otherwise, do nothing.
484      * @param addr - addr must be aligned to BYTESPERCHUNK.
485      * @return Returns old value of the bit.
486      */
AtomicTestAndSet(void * addr)487     bool AtomicTestAndSet(void *addr)
488     {
489         CheckAddrValidity(addr);
490         return AtomicTestAndSetBit(AddrToBitOffset(ToPointerType(addr)));
491     }
492 
493     /**
494      * \brief Atomically clear bit corresponding to addr. If the bit is set, clear it atomically. Otherwise, do nothing.
495      * @param addr - addr must be aligned to BYTESPERCHUNK.
496      * @return Returns old value of the bit.
497      */
AtomicTestAndClear(void * addr)498     bool AtomicTestAndClear(void *addr)
499     {
500         CheckAddrValidity(addr);
501         return AtomicTestAndClearBit(AddrToBitOffset(ToPointerType(addr)));
502     }
503 
504     /**
505      * \brief Atomically test bit corresponding to addr.
506      * @param addr - addr must be aligned to BYTESPERCHUNK.
507      * @return Returns the value of the bit.
508      */
AtomicTest(void * addr)509     bool AtomicTest(void *addr)
510     {
511         CheckAddrValidity(addr);
512         return AtomicTestBit(AddrToBitOffset(ToPointerType(addr)));
513     }
514 
515     /**
516      * \brief Find first marked chunk.
517      */
FindFirstMarkedChunks()518     void *FindFirstMarkedChunks()
519     {
520         void *first_marked = nullptr;
521         IterateOverSetBits([&first_marked, this](size_t bit_offset) {
522             first_marked = BitOffsetToAddr(bit_offset);
523             return false;
524         });
525         return first_marked;
526     }
527 
528     /**
529      * \brief Iterate over marked chunks of memory sequentially.
530      */
531     template <typename MemVisitor>
IterateOverMarkedChunks(const MemVisitor & visitor)532     void IterateOverMarkedChunks(const MemVisitor &visitor)
533     {
534         IterateOverSetBits([&visitor, this](size_t bit_offset) {
535             visitor(BitOffsetToAddr(bit_offset));
536             return true;
537         });
538     }
539 
540     /**
541      * \brief Iterate over all chunks of memory sequentially.
542      */
543     template <typename MemVisitor>
IterateOverChunks(const MemVisitor & visitor)544     void IterateOverChunks(const MemVisitor &visitor)
545     {
546         IterateOverBits([&visitor, this](size_t bit_offset) { visitor(BitOffsetToAddr(bit_offset)); });
547     }
548 
549     /**
550      * \brief Iterate over marked chunks of memory in range [begin, end) sequentially.
551      */
552     template <typename MemVisitor>
IterateOverMarkedChunkInRange(void * begin,void * end,const MemVisitor & visitor)553     void IterateOverMarkedChunkInRange(void *begin, void *end, const MemVisitor &visitor)
554     {
555         CheckHalfClosedHalfOpenAddressRange(begin, end);
556         IterateOverSetBitsInRange(AddrToBitOffset(ToPointerType(begin)), EndAddrToBitOffset(ToPointerType(end)),
557                                   [&visitor, this](size_t bit_offset) {
558                                       visitor(BitOffsetToAddr(bit_offset));
559                                       return true;
560                                   });
561     }
562 
563     /**
564      * \brief Iterate over all chunks of memory in range [begin, end) sequentially.
565      */
566     template <typename MemVisitor>
IterateOverChunkInRange(void * begin,void * end,const MemVisitor & visitor)567     void IterateOverChunkInRange(void *begin, void *end, const MemVisitor &visitor)
568     {
569         CheckHalfClosedHalfOpenAddressRange(begin, end);
570         IterateOverBitsInRange(AddrToBitOffset(ToPointerType(begin)), EndAddrToBitOffset(ToPointerType(end)),
571                                [&visitor, this](size_t bit_offset) { visitor(BitOffsetToAddr(bit_offset)); });
572     }
573 
IsAddrInRange(const void * addr)574     bool IsAddrInRange(const void *addr) const
575     {
576         return addr >= ToVoidPtr(begin_addr_) && addr < ToVoidPtr(end_addr_);
577     }
578 
579     template <class T>
ToPointerType(T * val)580     static constexpr pointer_type ToPointerType(T *val)
581     {
582         return static_cast<pointer_type>(ToUintPtr(val));
583     }
584 
585 private:
586     /**
587      * \brief Compute bit offset from addr.
588      */
AddrToBitOffset(pointer_type addr)589     size_t AddrToBitOffset(pointer_type addr) const
590     {
591         return (addr - begin_addr_) / BYTESPERCHUNK;
592     }
593 
EndAddrToBitOffset(pointer_type addr)594     size_t EndAddrToBitOffset(pointer_type addr) const
595     {
596         return (AlignUp(addr, BYTESPERCHUNK) - begin_addr_) / BYTESPERCHUNK;
597     }
598 
599     /**
600      * \brief Compute address from bit offset.
601      */
BitOffsetToAddr(size_t bit_offset)602     void *BitOffsetToAddr(size_t bit_offset) const
603     {
604         return ToVoidPtr(begin_addr_ + bit_offset * BYTESPERCHUNK);
605     }
606 
607     /**
608      * \brief Check if addr is valid.
609      */
CheckAddrValidity(const void * addr)610     void CheckAddrValidity([[maybe_unused]] const void *addr) const
611     {
612         ASSERT(IsAddrInRange(addr));
613         ASSERT((ToPointerType(addr) - begin_addr_) % BYTESPERCHUNK == 0);
614     }
615 
616     /**
617      * \brief Check if addr is valid with returned value.
618      * @return true if addr is valid
619      */
IsAddrValid(const void * addr)620     bool IsAddrValid(const void *addr) const
621     {
622         return IsAddrInRange(addr) && (ToPointerType(addr) - begin_addr_) % BYTESPERCHUNK == 0;
623     }
624 
625     /**
626      * \brief Check if [begin, end) is a valid address range.
627      */
CheckHalfClosedHalfOpenAddressRange(void * begin,void * end)628     void CheckHalfClosedHalfOpenAddressRange([[maybe_unused]] void *begin, [[maybe_unused]] void *end) const
629     {
630         CheckAddrValidity(begin);
631         ASSERT(ToPointerType(end) >= begin_addr_);
632         ASSERT(ToPointerType(end) <= end_addr_);
633         ASSERT(ToPointerType(begin) <= ToPointerType(end));
634     }
635 
636     pointer_type begin_addr_ {0};
637     pointer_type end_addr_ {0};
638 };
639 
640 using MarkBitmap = MemBitmap<DEFAULT_ALIGNMENT_IN_BYTES>;
641 
642 }  // namespace panda::mem
643 
644 #endif  // PANDA_RUNTIME_MEM_GC_BITMAP_H_
645