LCOV - code coverage report
Current view: top level - asmjit - zone.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 111 138 80.4 %
Date: 2026-11-05 10:19:06 Functions: 9 10 90.0 %

          Line data    Source code
       1             : #ifdef __PLUMED_HAS_ASMJIT
       2             : // This file is part of AsmJit project <https://asmjit.com>
       3             : //
       4             : // See asmjit.h or LICENSE.md for license and copyright information
       5             : // SPDX-License-Identifier: Zlib
       6             : 
       7             : #include "api-build_p.h"
       8             : #include "support.h"
       9             : #include "zone.h"
      10             : 
      11             : ASMJIT_BEGIN_NAMESPACE
      12             : 
      13             : // Zone - Globals
      14             : // ==============
      15             : 
      16             : // Zero size block used by `Zone` that doesn't have any memory allocated. Should be allocated in read-only memory
      17             : // and should never be modified.
      18             : const Zone::Block Zone::_zeroBlock = { nullptr, nullptr, 0 };
      19             : 
      20             : // Zone - Init & Reset
      21             : // ===================
      22             : 
      23      137460 : void Zone::_init(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept {
      24             :   ASMJIT_ASSERT(blockSize >= kMinBlockSize);
      25             :   ASMJIT_ASSERT(blockSize <= kMaxBlockSize);
      26             :   ASMJIT_ASSERT(blockAlignment <= 64);
      27             : 
      28             :   // Just to make the compiler happy...
      29             :   constexpr size_t kBlockSizeMask = (Support::allOnes<size_t>() >> 4);
      30             :   constexpr size_t kBlockAlignmentShiftMask = 0x7u;
      31             : 
      32             :   _assignZeroBlock();
      33      137460 :   _blockSize = blockSize & kBlockSizeMask;
      34      137460 :   _isTemporary = temporary != nullptr;
      35      137460 :   _blockAlignmentShift = Support::ctz(blockAlignment) & kBlockAlignmentShiftMask;
      36             : 
      37             :   // Setup the first [temporary] block, if necessary.
      38      137460 :   if (temporary) {
      39             :     Block* block = temporary->data<Block>();
      40           0 :     block->prev = nullptr;
      41           0 :     block->next = nullptr;
      42             : 
      43             :     ASMJIT_ASSERT(temporary->size() >= kBlockSize);
      44           0 :     block->size = temporary->size() - kBlockSize;
      45             : 
      46           0 :     _assignBlock(block);
      47             :   }
      48      137460 : }
      49             : 
      50      274920 : void Zone::reset(ResetPolicy resetPolicy) noexcept {
      51      274920 :   Block* cur = _block;
      52             : 
      53             :   // Can't be altered.
      54      274920 :   if (cur == &_zeroBlock)
      55             :     return;
      56             : 
      57      164952 :   if (resetPolicy == ResetPolicy::kHard) {
      58             :     Block* initial = const_cast<Zone::Block*>(&_zeroBlock);
      59      109968 :     _ptr = initial->data();
      60      109968 :     _end = initial->data();
      61      109968 :     _block = initial;
      62             : 
      63             :     // Since cur can be in the middle of the double-linked list, we have to traverse both directions (`prev` and
      64             :     // `next`) separately to visit all.
      65      109968 :     Block* next = cur->next;
      66             :     do {
      67      110670 :       Block* prev = cur->prev;
      68             : 
      69             :       // If this is the first block and this ZoneTmp is temporary then the first block is statically allocated.
      70             :       // We cannot free it and it makes sense to keep it even when this is hard reset.
      71      110670 :       if (prev == nullptr && _isTemporary) {
      72           0 :         cur->prev = nullptr;
      73           0 :         cur->next = nullptr;
      74           0 :         _assignBlock(cur);
      75           0 :         break;
      76             :       }
      77             : 
      78      110670 :       ::free(cur);
      79             :       cur = prev;
      80      110670 :     } while (cur);
      81             : 
      82             :     cur = next;
      83      110220 :     while (cur) {
      84         252 :       next = cur->next;
      85         252 :       ::free(cur);
      86             :       cur = next;
      87             :     }
      88             :   }
      89             :   else {
      90       55236 :     while (cur->prev)
      91             :       cur = cur->prev;
      92       54984 :     _assignBlock(cur);
      93             :   }
      94             : }
      95             : 
      96             : // Zone - Alloc
      97             : // ============
      98             : 
      99      110922 : void* Zone::_alloc(size_t size, size_t alignment) noexcept {
     100      110922 :   Block* curBlock = _block;
     101      110922 :   Block* next = curBlock->next;
     102             : 
     103             :   size_t rawBlockAlignment = blockAlignment();
     104             :   size_t minimumAlignment = Support::max<size_t>(alignment, rawBlockAlignment);
     105             : 
     106             :   // If the `Zone` has been cleared the current block doesn't have to be the last one. Check if there is a block
     107             :   // that can be used instead of allocating a new one. If there is a `next` block it's completely unused, we don't
     108             :   // have to check for remaining bytes in that case.
     109      110922 :   if (next) {
     110             :     uint8_t* ptr = Support::alignUp(next->data(), minimumAlignment);
     111           0 :     uint8_t* end = Support::alignDown(next->data() + next->size, rawBlockAlignment);
     112             : 
     113           0 :     if (size <= (size_t)(end - ptr)) {
     114           0 :       _block = next;
     115           0 :       _ptr = ptr + size;
     116           0 :       _end = Support::alignDown(next->data() + next->size, rawBlockAlignment);
     117           0 :       return static_cast<void*>(ptr);
     118             :     }
     119             :   }
     120             : 
     121      110922 :   size_t blockAlignmentOverhead = alignment - Support::min<size_t>(alignment, Globals::kAllocAlignment);
     122             :   size_t newSize = Support::max(blockSize(), size);
     123             : 
     124             :   // Prevent arithmetic overflow.
     125      110922 :   if (ASMJIT_UNLIKELY(newSize > SIZE_MAX - kBlockSize - blockAlignmentOverhead))
     126             :     return nullptr;
     127             : 
     128             :   // Allocate new block - we add alignment overhead to `newSize`, which becomes the new block size, and we also add
     129             :   // `kBlockOverhead` to the allocator as it includes members of `Zone::Block` structure.
     130      110922 :   newSize += blockAlignmentOverhead;
     131      110922 :   Block* newBlock = static_cast<Block*>(::malloc(newSize + kBlockSize));
     132             : 
     133      110922 :   if (ASMJIT_UNLIKELY(!newBlock))
     134             :     return nullptr;
     135             : 
     136             :   // Align the pointer to `minimumAlignment` and adjust the size of this block accordingly. It's the same as using
     137             :   // `minimumAlignment - Support::alignUpDiff()`, just written differently.
     138             :   {
     139      110922 :     newBlock->prev = nullptr;
     140      110922 :     newBlock->next = nullptr;
     141      110922 :     newBlock->size = newSize;
     142             : 
     143      110922 :     if (curBlock != &_zeroBlock) {
     144         954 :       newBlock->prev = curBlock;
     145         954 :       curBlock->next = newBlock;
     146             : 
     147             :       // Does only happen if there is a next block, but the requested memory can't fit into it. In this case a new
     148             :       // buffer is allocated and inserted between the current block and the next one.
     149         954 :       if (next) {
     150           0 :         newBlock->next = next;
     151           0 :         next->prev = newBlock;
     152             :       }
     153             :     }
     154             : 
     155             :     uint8_t* ptr = Support::alignUp(newBlock->data(), minimumAlignment);
     156      110922 :     uint8_t* end = Support::alignDown(newBlock->data() + newSize, rawBlockAlignment);
     157             : 
     158      110922 :     _ptr = ptr + size;
     159      110922 :     _end = end;
     160      110922 :     _block = newBlock;
     161             : 
     162             :     ASMJIT_ASSERT(_ptr <= _end);
     163      110922 :     return static_cast<void*>(ptr);
     164             :   }
     165             : }
     166             : 
     167      273777 : void* Zone::allocZeroed(size_t size, size_t alignment) noexcept {
     168      273777 :   void* p = alloc(size, alignment);
     169      273777 :   if (ASMJIT_UNLIKELY(!p))
     170             :     return p;
     171      273777 :   return memset(p, 0, size);
     172             : }
     173             : 
     174       54984 : void* Zone::dup(const void* data, size_t size, bool nullTerminate) noexcept {
     175       54984 :   if (ASMJIT_UNLIKELY(!data || !size))
     176             :     return nullptr;
     177             : 
     178             :   ASMJIT_ASSERT(size != SIZE_MAX);
     179       54984 :   uint8_t* m = allocT<uint8_t>(size + nullTerminate);
     180       54984 :   if (ASMJIT_UNLIKELY(!m)) return nullptr;
     181             : 
     182             :   memcpy(m, data, size);
     183       54984 :   if (nullTerminate) m[size] = '\0';
     184             : 
     185             :   return static_cast<void*>(m);
     186             : }
     187             : 
     188           0 : char* Zone::sformat(const char* fmt, ...) noexcept {
     189           0 :   if (ASMJIT_UNLIKELY(!fmt))
     190             :     return nullptr;
     191             : 
     192             :   char buf[512];
     193             :   size_t size;
     194             :   va_list ap;
     195             : 
     196           0 :   va_start(ap, fmt);
     197           0 :   size = unsigned(vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf) - 1, fmt, ap));
     198           0 :   va_end(ap);
     199             : 
     200           0 :   buf[size++] = 0;
     201           0 :   return static_cast<char*>(dup(buf, size));
     202             : }
     203             : 
     204             : // ZoneAllocator - Utilities
     205             : // =========================
     206             : 
     207             : #if defined(ASMJIT_BUILD_DEBUG)
     208             : static bool ZoneAllocator_hasDynamicBlock(ZoneAllocator* self, ZoneAllocator::DynamicBlock* block) noexcept {
     209             :   ZoneAllocator::DynamicBlock* cur = self->_dynamicBlocks;
     210             :   while (cur) {
     211             :     if (cur == block)
     212             :       return true;
     213             :     cur = cur->next;
     214             :   }
     215             :   return false;
     216             : }
     217             : #endif
     218             : 
     219             : // ZoneAllocator - Init & Reset
     220             : // ============================
     221             : 
     222      164952 : void ZoneAllocator::reset(Zone* zone) noexcept {
     223             :   // Free dynamic blocks.
     224      164952 :   DynamicBlock* block = _dynamicBlocks;
     225      209131 :   while (block) {
     226       44179 :     DynamicBlock* next = block->next;
     227       44179 :     ::free(block);
     228             :     block = next;
     229             :   }
     230             : 
     231             :   // Zero the entire class and initialize to the given `zone`.
     232             :   memset(this, 0, sizeof(*this));
     233      164952 :   _zone = zone;
     234      164952 : }
     235             : 
     236             : // asmjit::ZoneAllocator - Alloc & Release
     237             : // =======================================
     238             : 
     239     2796496 : void* ZoneAllocator::_alloc(size_t size, size_t& allocatedSize) noexcept {
     240             :   ASMJIT_ASSERT(isInitialized());
     241             : 
     242             :   // Use the memory pool only if the requested block has a reasonable size.
     243             :   uint32_t slot;
     244     2796496 :   if (_getSlotIndex(size, slot, allocatedSize)) {
     245             :     // Slot reuse.
     246     2738298 :     uint8_t* p = reinterpret_cast<uint8_t*>(_slots[slot]);
     247     2738298 :     size = allocatedSize;
     248             : 
     249     2738298 :     if (p) {
     250      250701 :       _slots[slot] = reinterpret_cast<Slot*>(p)->next;
     251      250701 :       return p;
     252             :     }
     253             : 
     254     2487597 :     _zone->align(kBlockAlignment);
     255     2487597 :     p = _zone->ptr();
     256     2487597 :     size_t remain = (size_t)(_zone->end() - p);
     257             : 
     258     2487597 :     if (ASMJIT_LIKELY(remain >= size)) {
     259     2432277 :       _zone->setPtr(p + size);
     260     2432277 :       return p;
     261             :     }
     262             :     else {
     263             :       // Distribute the remaining memory to suitable slots, if possible.
     264       55320 :       if (remain >= kLoGranularity) {
     265             :         do {
     266             :           size_t distSize = Support::min<size_t>(remain, kLoMaxSize);
     267         123 :           uint32_t distSlot = uint32_t((distSize - kLoGranularity) / kLoGranularity);
     268             :           ASMJIT_ASSERT(distSlot < kLoCount);
     269             : 
     270         123 :           reinterpret_cast<Slot*>(p)->next = _slots[distSlot];
     271         123 :           _slots[distSlot] = reinterpret_cast<Slot*>(p);
     272             : 
     273         123 :           p += distSize;
     274         123 :           remain -= distSize;
     275         123 :         } while (remain >= kLoGranularity);
     276         123 :         _zone->setPtr(p);
     277             :       }
     278             : 
     279       55320 :       p = static_cast<uint8_t*>(_zone->_alloc(size, kBlockAlignment));
     280       55320 :       if (ASMJIT_UNLIKELY(!p)) {
     281           0 :         allocatedSize = 0;
     282           0 :         return nullptr;
     283             :       }
     284             : 
     285             :       return p;
     286             :     }
     287             :   }
     288             :   else {
     289             :     // Allocate a dynamic block.
     290             :     size_t kBlockOverhead = sizeof(DynamicBlock) + sizeof(DynamicBlock*) + kBlockAlignment;
     291             : 
     292             :     // Handle a possible overflow.
     293       58198 :     if (ASMJIT_UNLIKELY(kBlockOverhead >= SIZE_MAX - size))
     294             :       return nullptr;
     295             : 
     296       58198 :     void* p = ::malloc(size + kBlockOverhead);
     297       58198 :     if (ASMJIT_UNLIKELY(!p)) {
     298           0 :       allocatedSize = 0;
     299           0 :       return nullptr;
     300             :     }
     301             : 
     302             :     // Link as first in `_dynamicBlocks` double-linked list.
     303             :     DynamicBlock* block = static_cast<DynamicBlock*>(p);
     304       58198 :     DynamicBlock* next = _dynamicBlocks;
     305             : 
     306       58198 :     if (next)
     307       30288 :       next->prev = block;
     308             : 
     309       58198 :     block->prev = nullptr;
     310       58198 :     block->next = next;
     311       58198 :     _dynamicBlocks = block;
     312             : 
     313             :     // Align the pointer to the guaranteed alignment and store `DynamicBlock`
     314             :     // at the beginning of the memory block, so `_releaseDynamic()` can find it.
     315       58198 :     p = Support::alignUp(static_cast<uint8_t*>(p) + sizeof(DynamicBlock) + sizeof(DynamicBlock*), kBlockAlignment);
     316       58198 :     reinterpret_cast<DynamicBlock**>(p)[-1] = block;
     317             : 
     318       58198 :     allocatedSize = size;
     319       58198 :     return p;
     320             :   }
     321             : }
     322             : 
     323       82476 : void* ZoneAllocator::_allocZeroed(size_t size, size_t& allocatedSize) noexcept {
     324             :   ASMJIT_ASSERT(isInitialized());
     325             : 
     326       82476 :   void* p = _alloc(size, allocatedSize);
     327       82476 :   if (ASMJIT_UNLIKELY(!p)) return p;
     328       82476 :   return memset(p, 0, allocatedSize);
     329             : }
     330             : 
     331       14019 : void ZoneAllocator::_releaseDynamic(void* p, size_t size) noexcept {
     332             :   DebugUtils::unused(size);
     333             :   ASMJIT_ASSERT(isInitialized());
     334             : 
     335             :   // Pointer to `DynamicBlock` is stored at [-1].
     336       14019 :   DynamicBlock* block = reinterpret_cast<DynamicBlock**>(p)[-1];
     337             :   ASMJIT_ASSERT(ZoneAllocator_hasDynamicBlock(this, block));
     338             : 
     339             :   // Unlink and free.
     340       14019 :   DynamicBlock* prev = block->prev;
     341       14019 :   DynamicBlock* next = block->next;
     342             : 
     343       14019 :   if (prev)
     344       13920 :     prev->next = next;
     345             :   else
     346          99 :     _dynamicBlocks = next;
     347             : 
     348       14019 :   if (next)
     349       13899 :     next->prev = prev;
     350             : 
     351       14019 :   ::free(block);
     352       14019 : }
     353             : 
     354             : ASMJIT_END_NAMESPACE
     355             : 
     356             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16