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