• 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