LCOV - code coverage report
Current view: top level - asmjit - jitallocator.h (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 1 6 16.7 %
Date: 2026-11-05 10:19:06 Functions: 0 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             : #ifndef ASMJIT_CORE_JITALLOCATOR_H_INCLUDED
       8             : #define ASMJIT_CORE_JITALLOCATOR_H_INCLUDED
       9             : 
      10             : #include "api-config.h"
      11             : #ifndef ASMJIT_NO_JIT
      12             : 
      13             : #include "globals.h"
      14             : #include "virtmem.h"
      15             : 
      16             : ASMJIT_BEGIN_NAMESPACE
      17             : 
      18             : //! \addtogroup asmjit_virtual_memory
      19             : //! \{
      20             : 
      21             : //! Options used by \ref JitAllocator.
      22             : enum class JitAllocatorOptions : uint32_t {
      23             :   //! No options.
      24             :   kNone = 0,
      25             : 
      26             :   //! Enables the use of an anonymous memory-mapped memory that is mapped into two buffers having a different pointer.
      27             :   //! The first buffer has read and execute permissions and the second buffer has read+write permissions.
      28             :   //!
      29             :   //! See \ref VirtMem::allocDualMapping() for more details about this feature.
      30             :   kUseDualMapping = 0x00000001u,
      31             : 
      32             :   //! Enables the use of multiple pools with increasing granularity instead of a single pool. This flag would enable
      33             :   //! 3 internal pools in total having 64, 128, and 256 bytes granularity.
      34             :   //!
      35             :   //! This feature is only recommended for users that generate a lot of code and would like to minimize the overhead
      36             :   //! of `JitAllocator` itself by having blocks of different allocation granularities. Using this feature only for
      37             :   //! few allocations won't pay off as the allocator may need to create more blocks initially before it can take the
      38             :   //! advantage of variable block granularity.
      39             :   kUseMultiplePools = 0x00000002u,
      40             : 
      41             :   //! Always fill reserved memory by a fill-pattern.
      42             :   //!
      43             :   //! Causes a new block to be cleared by the fill pattern and freshly released memory to be cleared before making
      44             :   //! it ready for another use.
      45             :   kFillUnusedMemory = 0x00000004u,
      46             : 
      47             :   //! When this flag is set the allocator would immediately release unused blocks during `release()` or `reset()`.
      48             :   //! When this flag is not set the allocator would keep one empty block in each pool to prevent excessive virtual
      49             :   //! memory allocations and deallocations in border cases, which involve constantly allocating and deallocating a
      50             :   //! single block caused by repetitive calling `alloc()` and `release()` when the allocator has either no blocks
      51             :   //! or have all blocks fully occupied.
      52             :   kImmediateRelease = 0x00000008u,
      53             : 
      54             :   //! Use a custom fill pattern, must be combined with `kFlagFillUnusedMemory`.
      55             :   kCustomFillPattern = 0x10000000u
      56             : };
      57       27422 : ASMJIT_DEFINE_ENUM_FLAGS(JitAllocatorOptions)
      58             : 
      59             : //! A simple implementation of memory manager that uses `asmjit::VirtMem`
      60             : //! functions to manage virtual memory for JIT compiled code.
      61             : //!
      62             : //! Implementation notes:
      63             : //!
      64             : //! - Granularity of allocated blocks is different than granularity for a typical C malloc. In addition, the allocator
      65             : //!   can use several memory pools having a different granularity to minimize the maintenance overhead. Multiple pools
      66             : //!   feature requires `kFlagUseMultiplePools` flag to be set.
      67             : //!
      68             : //! - The allocator doesn't store any information in executable memory, instead, the implementation uses two
      69             : //!   bit-vectors to manage allocated memory of each allocator-block. The first bit-vector called 'used' is used to
      70             : //!   track used memory (where each bit represents memory size defined by granularity) and the second bit vector called
      71             : //!   'stop' is used as a sentinel to mark where the allocated area ends.
      72             : //!
      73             : //! - Internally, the allocator also uses RB tree to keep track of all blocks across all pools. Each inserted block is
      74             : //!   added to the tree so it can be matched fast during `release()` and `shrink()`.
      75             : class JitAllocator {
      76             : public:
      77             :   ASMJIT_NONCOPYABLE(JitAllocator)
      78             : 
      79             :   struct Impl {
      80             :     //! Allocator options.
      81             :     JitAllocatorOptions options;
      82             :     //! Base block size (0 if the allocator is not initialized).
      83             :     uint32_t blockSize;
      84             :     //! Base granularity (0 if the allocator is not initialized).
      85             :     uint32_t granularity;
      86             :     //! A pattern that is used to fill unused memory if secure mode is enabled.
      87             :     uint32_t fillPattern;
      88             :   };
      89             : 
      90             :   //! Allocator implementation (private).
      91             :   Impl* _impl;
      92             : 
      93             :   //! \name Construction & Destruction
      94             :   //! \{
      95             : 
      96             :   //! Parameters that can be passed to `JitAllocator` constructor.
      97             :   //!
      98             :   //! Use it like this:
      99             :   //!
     100             :   //! ```
     101             :   //! // Zero initialize (zero means the default value) and change what you need.
     102             :   //! JitAllocator::CreateParams params {};
     103             :   //! params.blockSize = 1024 * 1024;
     104             :   //!
     105             :   //! // Create the allocator.
     106             :   //! JitAllocator allocator(&params);
     107             :   //! ```
     108             :   struct CreateParams {
     109             :     //! Allocator options.
     110             :     //!
     111             :     //! No options are used by default.
     112             :     JitAllocatorOptions options = JitAllocatorOptions::kNone;
     113             : 
     114             :     //! Base size of a single block in bytes (default 64kB).
     115             :     //!
     116             :     //! \remarks Block size must be equal to or greater than page size and must be power of 2. If the input is not
     117             :     //! valid then the default block size will be used instead.
     118             :     uint32_t blockSize = 0;
     119             : 
     120             :     //! Base granularity (and also natural alignment) of allocations in bytes (default 64).
     121             :     //!
     122             :     //! Since the `JitAllocator` uses bit-arrays to mark used memory the granularity also specifies how many bytes
     123             :     //! correspond to a single bit in such bit-array. Higher granularity means more waste of virtual memory (as it
     124             :     //! increases the natural alignment), but smaller bit-arrays as less bits would be required per a single block.
     125             :     uint32_t granularity = 0;
     126             : 
     127             :     //! Patter to use to fill unused memory.
     128             :     //!
     129             :     //! Only used if \ref JitAllocatorOptions::kCustomFillPattern is set.
     130             :     uint32_t fillPattern = 0;
     131             : 
     132             :     // Reset the content of `CreateParams`.
     133             :     inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
     134             :   };
     135             : 
     136             :   //! Creates a `JitAllocator` instance.
     137             :   ASMJIT_API explicit JitAllocator(const CreateParams* params = nullptr) noexcept;
     138             :   //! Destroys the `JitAllocator` instance and release all blocks held.
     139             :   ASMJIT_API ~JitAllocator() noexcept;
     140             : 
     141             :   inline bool isInitialized() const noexcept { return _impl->blockSize == 0; }
     142             : 
     143             :   //! Free all allocated memory - makes all pointers returned by `alloc()` invalid.
     144             :   //!
     145             :   //! \remarks This function is not thread-safe as it's designed to be used when nobody else is using allocator.
     146             :   //! The reason is that there is no point of calling `reset()` when the allocator is still in use.
     147             :   ASMJIT_API void reset(ResetPolicy resetPolicy = ResetPolicy::kSoft) noexcept;
     148             : 
     149             :   //! \}
     150             : 
     151             :   //! \name Accessors
     152             :   //! \{
     153             : 
     154             :   //! Returns allocator options, see `Flags`.
     155             :   inline JitAllocatorOptions options() const noexcept { return _impl->options; }
     156             :   //! Tests whether the allocator has the given `option` set.
     157             :   inline bool hasOption(JitAllocatorOptions option) const noexcept { return uint32_t(_impl->options & option) != 0; }
     158             : 
     159             :   //! Returns a base block size (a minimum size of block that the allocator would allocate).
     160             :   inline uint32_t blockSize() const noexcept { return _impl->blockSize; }
     161             :   //! Returns granularity of the allocator.
     162             :   inline uint32_t granularity() const noexcept { return _impl->granularity; }
     163             :   //! Returns pattern that is used to fill unused memory if `kFlagUseFillPattern` is set.
     164           0 :   inline uint32_t fillPattern() const noexcept { return _impl->fillPattern; }
     165             : 
     166             :   //! \}
     167             : 
     168             :   //! \name Alloc & Release
     169             :   //! \{
     170             : 
     171             :   //! Allocates a new memory block of the requested `size`.
     172             :   //!
     173             :   //! When the function is successful it stores two pointers in `rxPtrOut` and `rwPtrOut`. The pointers will be
     174             :   //! different only if `kOptionUseDualMapping` was used to setup the allocator (in that case the `rxPtrOut` would
     175             :   //! point to a Read+Execute region and `rwPtrOut` would point to a Read+Write region of the same memory-mapped block.
     176             :   ASMJIT_API Error alloc(void** rxPtrOut, void** rwPtrOut, size_t size) noexcept;
     177             : 
     178             :   //! Releases a memory block returned by `alloc()`.
     179             :   //!
     180             :   //! \remarks This function is thread-safe.
     181             :   ASMJIT_API Error release(void* rxPtr) noexcept;
     182             : 
     183             :   //! Frees extra memory allocated with `rxPtr` by shrinking it to the given `newSize`.
     184             :   //!
     185             :   //! \remarks This function is thread-safe.
     186             :   ASMJIT_API Error shrink(void* rxPtr, size_t newSize) noexcept;
     187             : 
     188             :   //! Queries information about an allocated memory block that contains the given `rxPtr`.
     189             :   //!
     190             :   //! The function returns `kErrorOk` when `rxPtr` is matched and fills `rxPtrOut`, `rwPtrOut`, and `sizeOut` output
     191             :   //! arguments. The returned `rxPtrOut` and `rwPtrOut` pointers point to the beginning of the block, and `sizeOut`
     192             :   //! describes the total amount of bytes this allocation uses - `sizeOut` will always be aligned to the allocation
     193             :   //! granularity, so for example if an allocation was 1 byte and the size granularity is 64, the returned `sizeOut`
     194             :   //! will be 64 bytes, because that's what the allocator sees.
     195             :   ASMJIT_API Error query(void* rxPtr, void** rxPtrOut, void** rwPtrOut, size_t* sizeOut) const noexcept;
     196             : 
     197             :   //! \}
     198             : 
     199             :   //! \name Statistics
     200             :   //! \{
     201             : 
     202             :   //! Statistics about `JitAllocator`.
     203             :   struct Statistics {
     204             :     //! Number of blocks `JitAllocator` maintains.
     205             :     size_t _blockCount;
     206             :     //! Number of active allocations.
     207             :     size_t _allocationCount;
     208             :     //! How many bytes are currently used / allocated.
     209             :     size_t _usedSize;
     210             :     //! How many bytes are currently reserved by the allocator.
     211             :     size_t _reservedSize;
     212             :     //! Allocation overhead (in bytes) required to maintain all blocks.
     213             :     size_t _overheadSize;
     214             : 
     215             :     inline void reset() noexcept {
     216           0 :       _blockCount = 0;
     217           0 :       _usedSize = 0;
     218           0 :       _reservedSize = 0;
     219           0 :       _overheadSize = 0;
     220             :     }
     221             : 
     222             :     //! Returns count of blocks managed by `JitAllocator` at the moment.
     223             :     inline size_t blockCount() const noexcept { return _blockCount; }
     224             :     //! Returns the number of active allocations.
     225             :     inline size_t allocationCount() const noexcept { return _allocationCount; }
     226             : 
     227             :     //! Returns how many bytes are currently used.
     228             :     inline size_t usedSize() const noexcept { return _usedSize; }
     229             :     //! Returns the number of bytes unused by the allocator at the moment.
     230             :     inline size_t unusedSize() const noexcept { return _reservedSize - _usedSize; }
     231             :     //! Returns the total number of bytes bytes reserved by the allocator (sum of sizes of all blocks).
     232             :     inline size_t reservedSize() const noexcept { return _reservedSize; }
     233             :     //! Returns the number of bytes the allocator needs to manage the allocated memory.
     234             :     inline size_t overheadSize() const noexcept { return _overheadSize; }
     235             : 
     236             :     inline double usedSizeAsPercent() const noexcept {
     237             :       return (double(usedSize()) / (double(reservedSize()) + 1e-16)) * 100.0;
     238             :     }
     239             : 
     240             :     inline double unusedSizeAsPercent() const noexcept {
     241             :       return (double(unusedSize()) / (double(reservedSize()) + 1e-16)) * 100.0;
     242             :     }
     243             : 
     244             :     inline double overheadSizeAsPercent() const noexcept {
     245             :       return (double(overheadSize()) / (double(reservedSize()) + 1e-16)) * 100.0;
     246             :     }
     247             :   };
     248             : 
     249             :   //! Returns JIT allocator statistics.
     250             :   //!
     251             :   //! \remarks This function is thread-safe.
     252             :   ASMJIT_API Statistics statistics() const noexcept;
     253             : 
     254             :   //! \}
     255             : };
     256             : 
     257             : //! \}
     258             : 
     259             : ASMJIT_END_NAMESPACE
     260             : 
     261             : #endif
     262             : #endif
     263             : 
     264             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16