1 // AsmJit - Machine code generation for C++ 2 // 3 // * Official AsmJit Home Page: https://asmjit.com 4 // * Official Github Repository: https://github.com/asmjit/asmjit 5 // 6 // Copyright (c) 2008-2020 The AsmJit Authors 7 // 8 // This software is provided 'as-is', without any express or implied 9 // warranty. In no event will the authors be held liable for any damages 10 // arising from the use of this software. 11 // 12 // Permission is granted to anyone to use this software for any purpose, 13 // including commercial applications, and to alter it and redistribute it 14 // freely, subject to the following restrictions: 15 // 16 // 1. The origin of this software must not be misrepresented; you must not 17 // claim that you wrote the original software. If you use this software 18 // in a product, an acknowledgment in the product documentation would be 19 // appreciated but is not required. 20 // 2. Altered source versions must be plainly marked as such, and must not be 21 // misrepresented as being the original software. 22 // 3. This notice may not be removed or altered from any source distribution. 23 24 #ifndef ASMJIT_CORE_JITALLOCATOR_H_INCLUDED 25 #define ASMJIT_CORE_JITALLOCATOR_H_INCLUDED 26 27 #include "../core/api-config.h" 28 #ifndef ASMJIT_NO_JIT 29 30 #include "../core/globals.h" 31 #include "../core/virtmem.h" 32 33 ASMJIT_BEGIN_NAMESPACE 34 35 //! \addtogroup asmjit_virtual_memory 36 //! \{ 37 38 // ============================================================================ 39 // [asmjit::JitAllocator] 40 // ============================================================================ 41 42 //! A simple implementation of memory manager that uses `asmjit::VirtMem` 43 //! functions to manage virtual memory for JIT compiled code. 44 //! 45 //! Implementation notes: 46 //! 47 //! - Granularity of allocated blocks is different than granularity for a typical 48 //! C malloc. In addition, the allocator can use several memory pools having a 49 //! different granularity to minimize the maintenance overhead. Multiple pools 50 //! feature requires `kFlagUseMultiplePools` flag to be set. 51 //! 52 //! - The allocator doesn't store any information in executable memory, instead, 53 //! the implementation uses two bit-vectors to manage allocated memory of each 54 //! allocator-block. The first bit-vector called 'used' is used to track used 55 //! memory (where each bit represents memory size defined by granularity) and 56 //! the second bit vector called 'stop' is used as a sentinel to mark where 57 //! the allocated area ends. 58 //! 59 //! - Internally, the allocator also uses RB tree to keep track of all blocks 60 //! across all pools. Each inserted block is added to the tree so it can be 61 //! matched fast during `release()` and `shrink()`. 62 class JitAllocator { 63 public: 64 ASMJIT_NONCOPYABLE(JitAllocator) 65 66 struct Impl { 67 //! Allocator options, see \ref JitAllocator::Options. 68 uint32_t options; 69 //! Base block size (0 if the allocator is not initialized). 70 uint32_t blockSize; 71 //! Base granularity (0 if the allocator is not initialized). 72 uint32_t granularity; 73 //! A pattern that is used to fill unused memory if secure mode is enabled. 74 uint32_t fillPattern; 75 }; 76 77 //! Allocator implementation (private). 78 Impl* _impl; 79 80 enum Options : uint32_t { 81 //! Enables the use of an anonymous memory-mapped memory that is mapped into 82 //! two buffers having a different pointer. The first buffer has read and 83 //! execute permissions and the second buffer has read+write permissions. 84 //! 85 //! See \ref VirtMem::allocDualMapping() for more details about this feature. 86 kOptionUseDualMapping = 0x00000001u, 87 88 //! Enables the use of multiple pools with increasing granularity instead of 89 //! a single pool. This flag would enable 3 internal pools in total having 90 //! 64, 128, and 256 bytes granularity. 91 //! 92 //! This feature is only recommended for users that generate a lot of code 93 //! and would like to minimize the overhead of `JitAllocator` itself by 94 //! having blocks of different allocation granularities. Using this feature 95 //! only for few allocations won't pay off as the allocator may need to 96 //! create more blocks initially before it can take the advantage of 97 //! variable block granularity. 98 kOptionUseMultiplePools = 0x00000002u, 99 100 //! Always fill reserved memory by a fill-pattern. 101 //! 102 //! Causes a new block to be cleared by the fill pattern and freshly 103 //! released memory to be cleared before making it ready for another use. 104 kOptionFillUnusedMemory = 0x00000004u, 105 106 //! When this flag is set the allocator would immediately release unused 107 //! blocks during `release()` or `reset()`. When this flag is not set the 108 //! allocator would keep one empty block in each pool to prevent excessive 109 //! virtual memory allocations and deallocations in border cases, which 110 //! involve constantly allocating and deallocating a single block caused 111 //! by repetitive calling `alloc()` and `release()` when the allocator has 112 //! either no blocks or have all blocks fully occupied. 113 kOptionImmediateRelease = 0x00000008u, 114 115 //! Use a custom fill pattern, must be combined with `kFlagFillUnusedMemory`. 116 kOptionCustomFillPattern = 0x10000000u 117 }; 118 119 //! \name Construction & Destruction 120 //! \{ 121 122 //! Parameters that can be passed to `JitAllocator` constructor. 123 //! 124 //! Use it like this: 125 //! 126 //! ``` 127 //! // Zero initialize (zero means the default value) and change what you need. 128 //! JitAllocator::CreateParams params {}; 129 //! params.blockSize = 1024 * 1024; 130 //! 131 //! // Create the allocator. 132 //! JitAllocator allocator(¶ms); 133 //! ``` 134 struct CreateParams { 135 //! Allocator options, see \ref JitAllocator::Options. 136 //! 137 //! No options are used by default. 138 uint32_t options; 139 140 //! Base size of a single block in bytes (default 64kB). 141 //! 142 //! \remarks Block size must be equal or greater to page size and must be 143 //! power of 2. If the input is not valid then the default block size will 144 //! be used instead. 145 uint32_t blockSize; 146 147 //! Base granularity (and also natural alignment) of allocations in bytes 148 //! (default 64). 149 //! 150 //! Since the `JitAllocator` uses bit-arrays to mark used memory the 151 //! granularity also specifies how many bytes correspond to a single bit in 152 //! such bit-array. Higher granularity means more waste of virtual memory 153 //! (as it increases the natural alignment), but smaller bit-arrays as less 154 //! bits would be required per a single block. 155 uint32_t granularity; 156 157 //! Patter to use to fill unused memory. 158 //! 159 //! Only used if \ref kOptionCustomFillPattern is set. 160 uint32_t fillPattern; 161 162 // Reset the content of `CreateParams`. resetCreateParams163 inline void reset() noexcept { memset(this, 0, sizeof(*this)); } 164 }; 165 166 //! Creates a `JitAllocator` instance. 167 explicit ASMJIT_API JitAllocator(const CreateParams* params = nullptr) noexcept; 168 //! Destroys the `JitAllocator` instance and release all blocks held. 169 ASMJIT_API ~JitAllocator() noexcept; 170 isInitialized()171 inline bool isInitialized() const noexcept { return _impl->blockSize == 0; } 172 173 //! Free all allocated memory - makes all pointers returned by `alloc()` invalid. 174 //! 175 //! \remarks This function is not thread-safe as it's designed to be used when 176 //! nobody else is using allocator. The reason is that there is no point of 177 //1 calling `reset()` when the allocator is still in use. 178 ASMJIT_API void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept; 179 180 //! \} 181 182 //! \name Accessors 183 //! \{ 184 185 //! Returns allocator options, see `Flags`. options()186 inline uint32_t options() const noexcept { return _impl->options; } 187 //! Tests whether the allocator has the given `option` set. hasOption(uint32_t option)188 inline bool hasOption(uint32_t option) const noexcept { return (_impl->options & option) != 0; } 189 190 //! Returns a base block size (a minimum size of block that the allocator would allocate). blockSize()191 inline uint32_t blockSize() const noexcept { return _impl->blockSize; } 192 //! Returns granularity of the allocator. granularity()193 inline uint32_t granularity() const noexcept { return _impl->granularity; } 194 //! Returns pattern that is used to fill unused memory if `kFlagUseFillPattern` is set. fillPattern()195 inline uint32_t fillPattern() const noexcept { return _impl->fillPattern; } 196 197 //! \} 198 199 //! \name Alloc & Release 200 //! \{ 201 202 //! Allocate `size` bytes of virtual memory. 203 //! 204 //! \remarks This function is thread-safe. 205 ASMJIT_API Error alloc(void** roPtrOut, void** rwPtrOut, size_t size) noexcept; 206 207 //! Release a memory returned by `alloc()`. 208 //! 209 //! \remarks This function is thread-safe. 210 ASMJIT_API Error release(void* roPtr) noexcept; 211 212 //! Free extra memory allocated with `p` by restricting it to `newSize` size. 213 //! 214 //! \remarks This function is thread-safe. 215 ASMJIT_API Error shrink(void* roPtr, size_t newSize) noexcept; 216 217 //! \} 218 219 //! \name Statistics 220 //! \{ 221 222 //! Statistics about `JitAllocator`. 223 struct Statistics { 224 //! Number of blocks `JitAllocator` maintains. 225 size_t _blockCount; 226 //! How many bytes are currently used / allocated. 227 size_t _usedSize; 228 //! How many bytes are currently reserved by the allocator. 229 size_t _reservedSize; 230 //! Allocation overhead (in bytes) required to maintain all blocks. 231 size_t _overheadSize; 232 resetStatistics233 inline void reset() noexcept { 234 _blockCount = 0; 235 _usedSize = 0; 236 _reservedSize = 0; 237 _overheadSize = 0; 238 } 239 240 //! Returns count of blocks managed by `JitAllocator` at the moment. blockCountStatistics241 inline size_t blockCount() const noexcept { return _blockCount; } 242 243 //! Returns how many bytes are currently used. usedSizeStatistics244 inline size_t usedSize() const noexcept { return _usedSize; } 245 //! Returns the number of bytes unused by the allocator at the moment. unusedSizeStatistics246 inline size_t unusedSize() const noexcept { return _reservedSize - _usedSize; } 247 //! Returns the total number of bytes bytes reserved by the allocator (sum of sizes of all blocks). reservedSizeStatistics248 inline size_t reservedSize() const noexcept { return _reservedSize; } 249 //! Returns the number of bytes the allocator needs to manage the allocated memory. overheadSizeStatistics250 inline size_t overheadSize() const noexcept { return _overheadSize; } 251 usedSizeAsPercentStatistics252 inline double usedSizeAsPercent() const noexcept { 253 return (double(usedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 254 } 255 unusedSizeAsPercentStatistics256 inline double unusedSizeAsPercent() const noexcept { 257 return (double(unusedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 258 } 259 overheadSizeAsPercentStatistics260 inline double overheadSizeAsPercent() const noexcept { 261 return (double(overheadSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 262 } 263 }; 264 265 //! Returns JIT allocator statistics. 266 //! 267 //! \remarks This function is thread-safe. 268 ASMJIT_API Statistics statistics() const noexcept; 269 270 //! \} 271 }; 272 273 //! \} 274 275 ASMJIT_END_NAMESPACE 276 277 #endif 278 #endif 279