• 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 /// Abstract base class. Constructor/destructor are protected. No virtual function to avoid dynamic polymorphism.
34 class Bitmap {
35 public:
36     using BitmapWordType = uintptr_t;
37 
Size()38     size_t Size() const
39     {
40         return bitsize_;
41     }
42 
ClearAllBits()43     void ClearAllBits()
44     {
45         memset_s(bitmap_.Data(), bitmap_.SizeBytes(), 0, bitmap_.SizeBytes());
46     }
47 
48     void CopyTo(Bitmap *dest) const;
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 bitOffset)65     void SetBit(size_t bitOffset)
66     {
67         CheckBitOffset(bitOffset);
68         bitmap_[GetWordIdx(bitOffset)] |= GetBitMask(bitOffset);
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 bitOffset)75     void ClearBit(size_t bitOffset)
76     {
77         CheckBitOffset(bitOffset);
78         bitmap_[GetWordIdx(bitOffset)] &= ~GetBitMask(bitOffset);
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 bitOffset)86     bool TestBit(size_t bitOffset) const
87     {
88         CheckBitOffset(bitOffset);
89         return (bitmap_[GetWordIdx(bitOffset)] & GetBitMask(bitOffset)) != 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 bitOffset);
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 bitOffset);
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 bitOffset);
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 js_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         auto bitmapWord = GetBitmapWord<ATOMIC>(begin);
143         auto offsetWithinWord = GetBitIdxWithinWord(begin);
144         // first word, clear bits before begin
145         bitmapWord &= GetRangeBitMask(offsetWithinWord, BITSPERWORD);
146         auto offsetWordBegin = GetWordIdx(begin) * BITSPERWORD;
147         const bool rightAligned = (GetBitIdxWithinWord(end) == 0);
148         const auto offsetLastWordBegin = GetWordIdx(end) * BITSPERWORD;
149         do {
150             if (offsetWordBegin == offsetLastWordBegin && !rightAligned) {
151                 // last partial word, clear bits after right boundary
152                 auto mask = GetRangeBitMask(0, GetBitIdxWithinWord(end));
153                 bitmapWord &= mask;
154             }
155             // loop over bits of bitmap_word
156             while (offsetWithinWord < BITSPERWORD) {
157                 if (bitmapWord == 0) {
158                     break;
159                 }
160                 offsetWithinWord = static_cast<size_t>(Ctz(bitmapWord));
161                 if (!visitor(offsetWordBegin + offsetWithinWord)) {
162                     return;
163                 }
164                 bitmapWord &= ~GetBitMask(offsetWithinWord);
165             }
166 
167             offsetWordBegin += BITSPERWORD;
168             if (offsetWordBegin >= end) {
169                 break;
170             }
171 
172             bitmapWord = GetBitmapWord<ATOMIC>(offsetWordBegin);
173             offsetWithinWord = 0;
174         } while (true);
175     }
176 
177     /**
178      * @brief Iterates over marked bits of bitmap sequentially.
179      * Finish iteration if the visitor returns false.
180      * @tparam atomic - true if want to iterate with atomic reading.
181      * @tparam VisitorType
182      * @param visitor - function pointer or functor.
183      */
184     template <bool ATOMIC, typename VisitorType>
IterateOverSetBits(const VisitorType & visitor)185     void IterateOverSetBits(const VisitorType &visitor)
186     {
187         IterateOverSetBitsInRange<ATOMIC, VisitorType>(0, Size(), visitor);
188     }
189 
190     /**
191      * @brief Iterates 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 wordBegin,size_t wordEnd)242     void SetWords([[maybe_unused]] size_t wordBegin, [[maybe_unused]] size_t wordEnd)
243     {
244         ASSERT(wordBegin <= wordEnd);
245         if (UNLIKELY(wordBegin == wordEnd)) {
246             return;
247         }
248         memset_s(&bitmap_[wordBegin], (wordEnd - wordBegin) * sizeof(BitmapWordType), ~static_cast<unsigned char>(0),
249                  (wordEnd - wordBegin) * sizeof(BitmapWordType));
250     }
251 
252     /**
253      * @brief Clear 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      */
ClearWords(size_t wordBegin,size_t wordEnd)257     void ClearWords([[maybe_unused]] size_t wordBegin, [[maybe_unused]] size_t wordEnd)
258     {
259         ASSERT(wordBegin <= wordEnd);
260         if (UNLIKELY(wordBegin == wordEnd)) {
261             return;
262         }
263         memset_s(&bitmap_[wordBegin], (wordEnd - wordBegin) * sizeof(BitmapWordType), static_cast<unsigned char>(0),
264                  (wordEnd - wordBegin) * sizeof(BitmapWordType));
265     }
266 
267     template <bool ATOMIC>
FindHighestPrecedingOrSameBit(size_t begin)268     size_t FindHighestPrecedingOrSameBit(size_t begin)
269     {
270         auto wordBeginOffset = GetWordIdx(begin) * BITSPERWORD;
271         auto offsetWithinWord = GetBitIdxWithinWord(begin);
272         auto mask = ~static_cast<BitmapWordType>(0) >> (BITSPERWORD - offsetWithinWord - 1);
273         auto bitmapWord = GetBitmapWord<ATOMIC>(begin) & mask;
274 
275         while (true) {
276             if (bitmapWord != 0) {
277                 offsetWithinWord = BITSPERWORD - static_cast<size_t>(Clz(bitmapWord)) - 1;
278                 return wordBeginOffset + offsetWithinWord;
279             }
280 
281             if (wordBeginOffset < BITSPERWORD) {
282                 break;
283             }
284 
285             wordBeginOffset -= BITSPERWORD;
286             bitmapWord = GetBitmapWord<ATOMIC>(wordBeginOffset);
287         }
288 
289         return begin;
290     }
291 
Bitmap(BitmapWordType * bitmap,size_t bitsize)292     explicit Bitmap(BitmapWordType *bitmap, size_t bitsize)
293         : bitmap_(bitmap, (AlignUp(bitsize, BITSPERWORD) >> LOG_BITSPERWORD)), bitsize_(bitsize)
294     {
295     }
296     ~Bitmap() = default;
297     // do we need special copy/move constructor?
298     NO_COPY_SEMANTIC(Bitmap);
299     NO_MOVE_SEMANTIC(Bitmap);
300 
301 private:
302     Span<BitmapWordType> bitmap_;
303     size_t bitsize_ = 0;
304 
305     /**
306      * @brief Compute word index from bit index.
307      * @param bit_offset - bit index.
308      * @return Returns BitmapWord Index of bit_offset.
309      */
GetWordIdx(size_t bitOffset)310     static size_t GetWordIdx(size_t bitOffset)
311     {
312         return bitOffset >> LOG_BITSPERWORD;
313     }
314 
315     /**
316      * @brief Compute bit index within a BitmapWord from bit index.
317      * @param bit_offset - bit index.
318      * @return Returns bit index within a BitmapWord.
319      */
GetBitIdxWithinWord(size_t bitOffset)320     size_t GetBitIdxWithinWord(size_t bitOffset) const
321     {
322         CheckBitOffset(bitOffset);
323         constexpr auto BIT_INDEX_MASK = static_cast<size_t>((1ULL << LOG_BITSPERWORD) - 1);
324         return bitOffset & BIT_INDEX_MASK;
325     }
326 
327     /**
328      * @brief Compute bit mask from bit index.
329      * @param bit_offset - bit index.
330      * @return Returns bit mask of bit_offset.
331      */
GetBitMask(size_t bitOffset)332     BitmapWordType GetBitMask(size_t bitOffset) const
333     {
334         return 1ULL << GetBitIdxWithinWord(bitOffset);
335     }
336 
337     /**
338      * @brief Compute bit mask of range [begin_within_word, end_within_word).
339      * @param begin_within_word - beginning index within word, in range [0, BITSPERWORD).
340      * @param end_within_word - end index within word, in range [0, BITSPERWORD]. Make sure end_within_word is
341      * BITSPERWORD(instead of 0) if you want to cover from certain bit to last. [0, 0) is the only valid case when
342      * end_within_word is 0.
343      * @return Returns bit mask.
344      */
GetRangeBitMask(size_t beginWithinWord,size_t endWithinWord)345     BitmapWordType GetRangeBitMask(size_t beginWithinWord, size_t endWithinWord) const
346     {
347         ASSERT(beginWithinWord < BITSPERWORD);
348         ASSERT(endWithinWord <= BITSPERWORD);
349         ASSERT(beginWithinWord <= endWithinWord);
350         auto endMask = (endWithinWord == BITSPERWORD) ? ~static_cast<BitmapWordType>(0) : GetBitMask(endWithinWord) - 1;
351         return endMask - (GetBitMask(beginWithinWord) - 1);
352     }
353 
354     /// @brief Check if bit_offset is valid.
CheckBitOffset(size_t bitOffset)355     void CheckBitOffset([[maybe_unused]] size_t bitOffset) const
356     {
357         ASSERT(bitOffset <= Size());
358     }
359 
360     /**
361      * @brief According to SET, set or clear range [begin, end) within a BitmapWord.
362      * @param begin - beginning global bit index.
363      * @param end - end global bit index.
364      */
365     template <bool SET>
ModifyRangeWithinWord(size_t begin,size_t end)366     ALWAYS_INLINE void ModifyRangeWithinWord(size_t begin, size_t end)
367     {
368         CheckBitRange(begin, end);
369 
370         if (UNLIKELY(begin == end)) {
371             return;
372         }
373 
374         BitmapWordType mask;
375         if (end % BITSPERWORD == 0) {
376             ASSERT(GetWordIdx(end) - GetWordIdx(begin) == 1);
377             mask = GetRangeBitMask(GetBitIdxWithinWord(begin), BITSPERWORD);
378         } else {
379             ASSERT(GetWordIdx(end) == GetWordIdx(begin));
380             mask = GetRangeBitMask(GetBitIdxWithinWord(begin), GetBitIdxWithinWord(end));
381         }
382 
383         if (SET) {
384             bitmap_[GetWordIdx(begin)] |= mask;
385         } else {
386             bitmap_[GetWordIdx(begin)] &= ~mask;
387         }
388     }
389 
390     /// @brief Check if bit range [begin, end) is valid.
CheckBitRange(size_t begin,size_t end)391     void CheckBitRange([[maybe_unused]] size_t begin, [[maybe_unused]] size_t end) const
392     {
393         ASSERT(begin < Size());
394         ASSERT(end <= Size());
395         ASSERT(begin <= end);
396     }
397 
398     template <bool ATOMIC>
GetBitmapWord(size_t bitOffset)399     BitmapWordType GetBitmapWord(size_t bitOffset)
400     {
401         auto index = GetWordIdx(bitOffset);
402         if constexpr (!ATOMIC) {
403             return bitmap_[index];
404         }
405 
406         auto *wordAddr = reinterpret_cast<std::atomic<BitmapWordType> *>(&bitmap_[index]);
407         // Atomic with seq_cst order reason: datarace with AtomicTestAndSet
408         return wordAddr->load(std::memory_order_seq_cst);
409     }
410 };
411 
412 /**
413  * Memory bitmap, binding a continuous range of memory to a bitmap.
414  * One bit represents BYTESPERCHUNK bytes of memory.
415  */
416 template <size_t BYTESPERCHUNK = 1, typename PointerType = ObjectPointerType>
417 class MemBitmap : public Bitmap {
418 public:
MemBitmap(void * memAddr,size_t heapSize,void * bitmapAddr)419     explicit MemBitmap(void *memAddr, size_t heapSize, void *bitmapAddr)
420         : Bitmap(static_cast<BitmapWordType *>(bitmapAddr), heapSize / BYTESPERCHUNK),
421           beginAddr_(ToPointerType(memAddr)),
422           endAddr_(beginAddr_ + heapSize)
423     {
424     }
425     NO_COPY_SEMANTIC(MemBitmap);
426     NO_MOVE_SEMANTIC(MemBitmap);
427 
428     /**
429      * @brief Reinitialize the MemBitmap for new memory range.
430      * The size of range will be the same as the initial
431      * because we reuse the same bitmap storage.
432      * @param mem_addr - start addr of the new range.
433      */
ReInitializeMemoryRange(void * memAddr)434     void ReInitializeMemoryRange(void *memAddr)
435     {
436         beginAddr_ = ToPointerType(memAddr);
437         endAddr_ = beginAddr_ + MemSizeInBytes();
438         Bitmap::ClearAllBits();
439     }
440 
GetBitMapSizeInByte(size_t heapSize)441     inline static constexpr size_t GetBitMapSizeInByte(size_t heapSize)
442     {
443         ASSERT(heapSize % BYTESPERCHUNK == 0);
444         size_t bitSize = heapSize / BYTESPERCHUNK;
445         return (AlignUp(bitSize, BITSPERWORD) >> Bitmap::LOG_BITSPERWORD) * sizeof(BitmapWordType);
446     }
447 
448     ~MemBitmap() = default;
449 
MemSizeInBytes()450     size_t MemSizeInBytes() const
451     {
452         return Size() * BYTESPERCHUNK;
453     }
454 
GetHeapRange()455     inline std::pair<uintptr_t, uintptr_t> GetHeapRange()
456     {
457         return {beginAddr_, endAddr_};
458     }
459 
460     /**
461      * @brief Set bit corresponding to addr.
462      * @param addr - addr must be aligned to BYTESPERCHUNK.
463      */
Set(void * addr)464     void Set(void *addr)
465     {
466         CheckAddrValidity(addr);
467         SetBit(AddrToBitOffset(ToPointerType(addr)));
468     }
469 
470     /**
471      * @brief Clear bit corresponding to addr.
472      * @param addr - addr must be aligned to BYTESPERCHUNK.
473      */
Clear(void * addr)474     void Clear(void *addr)
475     {
476         CheckAddrValidity(addr);
477         ClearBit(AddrToBitOffset(ToPointerType(addr)));
478     }
479 
480     /// @brief Clear bits corresponding to addr range [begin, end).
ClearRange(void * begin,void * end)481     ALWAYS_INLINE void ClearRange(void *begin, void *end)
482     {
483         CheckHalfClosedHalfOpenAddressRange(begin, end);
484         ClearBitsInRange(AddrToBitOffset(ToPointerType(begin)), EndAddrToBitOffset(ToPointerType(end)));
485     }
486 
487     /**
488      * @brief Test bit corresponding to addr.
489      * @param addr - addr must be aligned to BYTESPERCHUNK.
490      */
Test(const void * addr)491     bool Test(const void *addr) const
492     {
493         CheckAddrValidity(addr);
494         return TestBit(AddrToBitOffset(ToPointerType(addr)));
495     }
496 
497     /**
498      * @brief Test bit corresponding to addr if addr is valid.
499      * @return value of indexed bit if addr is valid. If addr is invalid then false
500      */
TestIfAddrValid(const void * addr)501     bool TestIfAddrValid(const void *addr) const
502     {
503         if (IsAddrValid(addr)) {
504             return TestBit(AddrToBitOffset(ToPointerType(addr)));
505         }
506         return false;
507     }
508 
509     /**
510      * @brief Atomically set bit corresponding to addr. If the bit is not set, set it atomically. Otherwise, do nothing.
511      * @param addr - addr must be aligned to BYTESPERCHUNK.
512      * @return Returns old value of the bit.
513      */
AtomicTestAndSet(void * addr)514     bool AtomicTestAndSet(void *addr)
515     {
516         CheckAddrValidity(addr);
517         return AtomicTestAndSetBit(AddrToBitOffset(ToPointerType(addr)));
518     }
519 
520     /**
521      * @brief Atomically clear bit corresponding to addr. If the bit is set, clear it atomically. Otherwise, do nothing.
522      * @param addr - addr must be aligned to BYTESPERCHUNK.
523      * @return Returns old value of the bit.
524      */
AtomicTestAndClear(void * addr)525     bool AtomicTestAndClear(void *addr)
526     {
527         CheckAddrValidity(addr);
528         return AtomicTestAndClearBit(AddrToBitOffset(ToPointerType(addr)));
529     }
530 
531     /**
532      * @brief Atomically test bit corresponding to addr.
533      * @param addr - addr must be aligned to BYTESPERCHUNK.
534      * @return Returns the value of the bit.
535      */
AtomicTest(const void * addr)536     bool AtomicTest(const void *addr)
537     {
538         CheckAddrValidity(addr);
539         return AtomicTestBit(AddrToBitOffset(ToPointerType(addr)));
540     }
541 
542     /// @brief Find first marked chunk.
543     template <bool ATOMIC = false>
FindFirstMarkedChunks()544     void *FindFirstMarkedChunks()
545     {
546         void *firstMarked = nullptr;
547         IterateOverSetBits<ATOMIC>([&firstMarked, this](size_t bitOffset) {
548             firstMarked = BitOffsetToAddr(bitOffset);
549             return false;
550         });
551         return firstMarked;
552     }
553 
554     /// @brief Iterates over marked chunks of memory sequentially.
555     template <bool ATOMIC = false, typename MemVisitor>
IterateOverMarkedChunks(const MemVisitor & visitor)556     void IterateOverMarkedChunks(const MemVisitor &visitor)
557     {
558         IterateOverSetBits<ATOMIC>([&visitor, this](size_t bitOffset) {
559             visitor(BitOffsetToAddr(bitOffset));
560             return true;
561         });
562     }
563 
564     /// @brief Iterates over all chunks of memory sequentially.
565     template <typename MemVisitor>
IterateOverChunks(const MemVisitor & visitor)566     void IterateOverChunks(const MemVisitor &visitor)
567     {
568         IterateOverBits([&visitor, this](size_t bitOffset) { visitor(BitOffsetToAddr(bitOffset)); });
569     }
570 
571     /// @brief Iterates over marked chunks of memory in range [begin, end) sequentially.
572     template <bool ATOMIC = false, typename MemVisitor, typename T = void *>
IterateOverMarkedChunkInRange(void * begin,void * end,const MemVisitor & visitor)573     void IterateOverMarkedChunkInRange(void *begin, void *end, const MemVisitor &visitor)
574     {
575         IterateOverMarkedChunkInRangeInterruptible<ATOMIC>(begin, end, [&visitor](void *obj) {
576             visitor(obj);
577             return true;
578         });
579     }
580 
581     /**
582      * @brief Iterates over marked chunks of memory in range [begin, end) sequentially and stops iteration if visitor
583      * returns false
584      */
585     template <bool ATOMIC = false, typename MemVisitor, typename T = void *>
IterateOverMarkedChunkInRangeInterruptible(void * begin,void * end,const MemVisitor & visitor)586     void IterateOverMarkedChunkInRangeInterruptible(void *begin, void *end, const MemVisitor &visitor)
587     {
588         CheckHalfClosedHalfOpenAddressRange(begin, end);
589         IterateOverSetBitsInRange<ATOMIC>(FindHighestPrecedingOrSameBit<ATOMIC>(AddrToBitOffset(ToPointerType(begin))),
590                                           EndAddrToBitOffset(ToPointerType(end)), [&visitor, this](size_t bitOffset) {
591                                               return visitor(BitOffsetToAddr(bitOffset));
592                                           });
593     }
594 
595     /// @brief Call visitor for single allocated object in humongous region
596     template <bool ATOMIC, typename MemVisitor>
CallForMarkedChunkInHumongousRegion(void * begin,const MemVisitor & visitor)597     void CallForMarkedChunkInHumongousRegion(void *begin, const MemVisitor &visitor)
598     {
599         bool marked;
600         if constexpr (ATOMIC) {
601             marked = AtomicTest(begin);
602         } else {
603             marked = Test(begin);
604         }
605         if (marked) {
606             visitor(begin);
607         }
608     }
609 
610     /// @brief Iterates over all chunks of memory in range [begin, end) sequentially.
611     template <typename MemVisitor>
IterateOverChunkInRange(void * begin,void * end,const MemVisitor & visitor)612     void IterateOverChunkInRange(void *begin, void *end, const MemVisitor &visitor)
613     {
614         CheckHalfClosedHalfOpenAddressRange(begin, end);
615         IterateOverBitsInRange(AddrToBitOffset(ToPointerType(begin)), EndAddrToBitOffset(ToPointerType(end)),
616                                [&visitor, this](size_t bitOffset) { visitor(BitOffsetToAddr(bitOffset)); });
617     }
618 
IsAddrInRange(const void * addr)619     bool IsAddrInRange(const void *addr) const
620     {
621         return addr >= ToVoidPtr(beginAddr_) && addr < ToVoidPtr(endAddr_);
622     }
623 
624     template <class T>
ToPointerType(T * val)625     static constexpr PointerType ToPointerType(T *val)
626     {
627         return static_cast<PointerType>(ToUintPtr(val));
628     }
629 
630 private:
631     /// @brief Computes bit offset from addr.
AddrToBitOffset(PointerType addr)632     size_t AddrToBitOffset(PointerType addr) const
633     {
634         return (addr - beginAddr_) / BYTESPERCHUNK;
635     }
636 
EndAddrToBitOffset(PointerType addr)637     size_t EndAddrToBitOffset(PointerType addr) const
638     {
639         return (AlignUp(addr, BYTESPERCHUNK) - beginAddr_) / BYTESPERCHUNK;
640     }
641 
642     /// @brief Computes address from bit offset.
BitOffsetToAddr(size_t bitOffset)643     void *BitOffsetToAddr(size_t bitOffset) const
644     {
645         return ToVoidPtr(beginAddr_ + bitOffset * BYTESPERCHUNK);
646     }
647 
648     /// @brief Check if addr is valid.
CheckAddrValidity(const void * addr)649     void CheckAddrValidity([[maybe_unused]] const void *addr) const
650     {
651         ASSERT(IsAddrInRange(addr));
652         ASSERT((ToPointerType(addr) - beginAddr_) % BYTESPERCHUNK == 0);
653     }
654 
655     /**
656      * @brief Check if addr is valid with returned value.
657      * @return true if addr is valid
658      */
IsAddrValid(const void * addr)659     bool IsAddrValid(const void *addr) const
660     {
661         return IsAddrInRange(addr) && (ToPointerType(addr) - beginAddr_) % BYTESPERCHUNK == 0;
662     }
663 
664     /// @brief Check if [begin, end) is a valid address range.
CheckHalfClosedHalfOpenAddressRange(void * begin,void * end)665     void CheckHalfClosedHalfOpenAddressRange([[maybe_unused]] void *begin, [[maybe_unused]] void *end) const
666     {
667         CheckAddrValidity(begin);
668         ASSERT(ToPointerType(end) >= beginAddr_);
669         ASSERT(ToPointerType(end) <= endAddr_);
670         ASSERT(ToPointerType(begin) <= ToPointerType(end));
671     }
672 
673     PointerType beginAddr_ {0};
674     PointerType endAddr_ {0};
675 };
676 
677 using MarkBitmap = MemBitmap<DEFAULT_ALIGNMENT_IN_BYTES>;
678 
679 }  // namespace panda::mem
680 #endif  // PANDA_MEM_GC_BITMAP_H
681