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