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(&params);
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* ro) 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* ro, 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