summaryrefslogtreecommitdiff
path: root/lib/AsmJit/MemoryManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/AsmJit/MemoryManager.cpp')
-rw-r--r--lib/AsmJit/MemoryManager.cpp1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/lib/AsmJit/MemoryManager.cpp b/lib/AsmJit/MemoryManager.cpp
new file mode 100644
index 0000000..cb6ffa9
--- /dev/null
+++ b/lib/AsmJit/MemoryManager.cpp
@@ -0,0 +1,1227 @@
+// AsmJit - Complete JIT Assembler for C++ Language.
+
+// Copyright (c) 2008-2010, Petr Kobalicek <kobalicek.petr@gmail.com>
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+// [Dependencies]
+#include "Build.h"
+#include "MemoryManager.h"
+#include "Platform.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <new>
+
+// [Api-Begin]
+#include "ApiBegin.h"
+
+// This file contains implementation of virtual memory management for AsmJit
+// library. The initial concept is to keep this implementation simple but
+// efficient. There are several goals I decided to write implementation myself.
+//
+// Goals:
+// - We need usually to allocate blocks of 64 bytes long and more.
+// - Alignment of allocated blocks is large - 32 bytes or 64 bytes.
+// - Keep memory manager information outside allocated virtual memory pages
+// (these pages allows execution of code).
+// - Keep implementation small.
+//
+// I think that implementation is not small and probably not too much readable,
+// so there is small know how.
+//
+// - Implementation is based on bit arrays and binary trees. Bit arrays
+// contains information about allocated and unused blocks of memory. Each
+// block size describes MemNode::density member. Count of blocks are
+// stored in MemNode::blocks member. For example if density is 64 and
+// count of blocks is 20, memory node contains 64*20 bytes of memory and
+// smallest possible allocation (and also alignment) is 64 bytes. So density
+// describes also memory alignment. Binary trees are used to enable fast
+// lookup into all addresses allocated by memory manager instance. This is
+// used mainly in MemoryManagerPrivate::free().
+//
+// Bit array looks like this (empty = unused, X = used) - Size of block 64
+// -------------------------------------------------------------------------
+// | |X|X| | | | | |X|X|X|X|X|X| | | | | | | | | | | | |X| | | | |X|X|X| | |
+// -------------------------------------------------------------------------
+// Bits array shows that there are 12 allocated blocks of 64 bytes, so total
+// allocated size is 768 bytes. Maximum count of continuous blocks is 12
+// (see largest gap).
+
+namespace AsmJit {
+
+// ============================================================================
+// [Bits Manipulation]
+// ============================================================================
+
+#define BITS_PER_ENTITY (sizeof(sysuint_t) * 8)
+
+static void _SetBit(sysuint_t* buf, sysuint_t index) ASMJIT_NOTHROW
+{
+ sysuint_t i = index / BITS_PER_ENTITY; // sysuint_t[]
+ sysuint_t j = index % BITS_PER_ENTITY; // sysuint_t[][] bit index
+
+ buf += i;
+ *buf |= (sysuint_t)1 << j;
+}
+
+static void _ClearBit(sysuint_t* buf, sysuint_t index) ASMJIT_NOTHROW
+{
+ sysuint_t i = index / BITS_PER_ENTITY; // sysuint_t[]
+ sysuint_t j = index % BITS_PER_ENTITY; // sysuint_t[][] bit index
+
+ buf += i;
+ *buf &= ~((sysuint_t)1 << j);
+}
+
+static void _SetBits(sysuint_t* buf, sysuint_t index, sysuint_t len) ASMJIT_NOTHROW
+{
+ if (len == 0) return;
+
+ sysuint_t i = index / BITS_PER_ENTITY; // sysuint_t[]
+ sysuint_t j = index % BITS_PER_ENTITY; // sysuint_t[][] bit index
+
+ // How many bytes process in the first group.
+ sysuint_t c = BITS_PER_ENTITY - j;
+ if (c > len) c = len;
+
+ // Offset.
+ buf += i;
+
+ *buf++ |= (((sysuint_t)-1) >> (BITS_PER_ENTITY - c)) << j;
+ len -= c;
+
+ while (len >= BITS_PER_ENTITY)
+ {
+ *buf++ = (sysuint_t)-1;
+ len -= BITS_PER_ENTITY;
+ }
+
+ if (len)
+ {
+ *buf |= (((sysuint_t)-1) >> (BITS_PER_ENTITY - len));
+ }
+}
+
+static void _ClearBits(sysuint_t* buf, sysuint_t index, sysuint_t len) ASMJIT_NOTHROW
+{
+ if (len == 0) return;
+
+ sysuint_t i = index / BITS_PER_ENTITY; // sysuint_t[]
+ sysuint_t j = index % BITS_PER_ENTITY; // sysuint_t[][] bit index
+
+ // How many bytes process in the first group.
+ sysuint_t c = BITS_PER_ENTITY - j;
+ if (c > len) c = len;
+
+ // Offset.
+ buf += i;
+
+ *buf++ &= ~((((sysuint_t)-1) >> (BITS_PER_ENTITY - c)) << j);
+ len -= c;
+
+ while (len >= BITS_PER_ENTITY)
+ {
+ *buf++ = 0;
+ len -= BITS_PER_ENTITY;
+ }
+
+ if (len)
+ {
+ *buf &= ((sysuint_t)-1) << len;
+ }
+}
+
+// ============================================================================
+// [AsmJit::MemNode]
+// ============================================================================
+
+#define M_DIV(x, y) ((x) / (y))
+#define M_MOD(x, y) ((x) % (y))
+
+template<typename T>
+struct ASMJIT_HIDDEN RbNode
+{
+ // --------------------------------------------------------------------------
+ // [Node red-black tree tree, key is mem pointer].
+ // --------------------------------------------------------------------------
+
+ // Implementation is based on article by Julienne Walker (Public Domain),
+ // including C code and original comments. Thanks for the excellent article.
+
+ // Left[0] and right[1] nodes.
+ T* node[2];
+ // Whether the node is RED.
+ uint32_t red;
+
+ // --------------------------------------------------------------------------
+ // [Chunk Memory]
+ // --------------------------------------------------------------------------
+
+ // Virtual memory address.
+ uint8_t* mem;
+};
+
+// Get whether the node is red (NULL or node with red flag).
+template<typename T>
+inline bool isRed(RbNode<T>* node)
+{
+ return node != NULL && node->red;
+}
+
+struct ASMJIT_HIDDEN MemNode : public RbNode<MemNode>
+{
+ // --------------------------------------------------------------------------
+ // [Node double-linked list]
+ // --------------------------------------------------------------------------
+
+ MemNode* prev; // Prev node in list.
+ MemNode* next; // Next node in list.
+
+ // --------------------------------------------------------------------------
+ // [Chunk Data]
+ // --------------------------------------------------------------------------
+
+ sysuint_t size; // How many bytes contain this node.
+ sysuint_t blocks; // How many blocks are here.
+ sysuint_t density; // Minimum count of allocated bytes in this node (also alignment).
+ sysuint_t used; // How many bytes are used in this node.
+ sysuint_t largestBlock; // Contains largest block that can be allocated.
+
+ sysuint_t* baUsed; // Contains bits about used blocks.
+ // (0 = unused, 1 = used).
+ sysuint_t* baCont; // Contains bits about continuous blocks.
+ // (0 = stop, 1 = continue).
+
+ // --------------------------------------------------------------------------
+ // [Methods]
+ // --------------------------------------------------------------------------
+
+ // Get available space.
+ inline sysuint_t getAvailable() const ASMJIT_NOTHROW { return size - used; }
+
+ inline void fillData(MemNode* other)
+ {
+ mem = other->mem;
+
+ size = other->size;
+ blocks = other->blocks;
+ density = other->density;
+ used = other->used;
+ largestBlock = other->largestBlock;
+ baUsed = other->baUsed;
+ baCont = other->baCont;
+ }
+};
+
+// ============================================================================
+// [AsmJit::M_Permanent]
+// ============================================================================
+
+//! @brief Permanent node.
+struct ASMJIT_HIDDEN PermanentNode
+{
+ uint8_t* mem; // Base pointer (virtual memory address).
+ sysuint_t size; // Count of bytes allocated.
+ sysuint_t used; // Count of bytes used.
+ PermanentNode* prev; // Pointer to prev chunk or NULL.
+
+ // Get available space.
+ inline sysuint_t getAvailable() const ASMJIT_NOTHROW { return size - used; }
+};
+
+// ============================================================================
+// [AsmJit::MemoryManagerPrivate]
+// ============================================================================
+
+struct ASMJIT_HIDDEN MemoryManagerPrivate
+{
+ // --------------------------------------------------------------------------
+ // [Construction / Destruction]
+ // --------------------------------------------------------------------------
+
+#if !defined(ASMJIT_WINDOWS)
+ MemoryManagerPrivate() ASMJIT_NOTHROW;
+#else
+ MemoryManagerPrivate(HANDLE hProcess) ASMJIT_NOTHROW;
+#endif // ASMJIT_WINDOWS
+ ~MemoryManagerPrivate() ASMJIT_NOTHROW;
+
+ // --------------------------------------------------------------------------
+ // [Allocation]
+ // --------------------------------------------------------------------------
+
+ MemNode* createNode(sysuint_t size, sysuint_t density) ASMJIT_NOTHROW;
+
+ void* allocPermanent(sysuint_t vsize) ASMJIT_NOTHROW;
+ void* allocFreeable(sysuint_t vsize) ASMJIT_NOTHROW;
+
+ bool free(void* address) ASMJIT_NOTHROW;
+ bool shrink(void* address, sysuint_t used) ASMJIT_NOTHROW;
+ void freeAll(bool keepVirtualMemory) ASMJIT_NOTHROW;
+
+ // Helpers to avoid ifdefs in the code.
+ inline uint8_t* allocVirtualMemory(sysuint_t size, sysuint_t* vsize) ASMJIT_NOTHROW
+ {
+#if !defined(ASMJIT_WINDOWS)
+ return (uint8_t*)VirtualMemory::alloc(size, vsize, true);
+#else
+ return (uint8_t*)VirtualMemory::allocProcessMemory(_hProcess, size, vsize, true);
+#endif
+ }
+
+ inline void freeVirtualMemory(void* vmem, sysuint_t vsize) ASMJIT_NOTHROW
+ {
+#if !defined(ASMJIT_WINDOWS)
+ VirtualMemory::free(vmem, vsize);
+#else
+ VirtualMemory::freeProcessMemory(_hProcess, vmem, vsize);
+#endif
+ }
+
+ // --------------------------------------------------------------------------
+ // [NodeList RB-Tree]
+ // --------------------------------------------------------------------------
+
+ bool checkTree() ASMJIT_NOTHROW;
+
+ void insertNode(MemNode* node) ASMJIT_NOTHROW;
+ MemNode* removeNode(MemNode* node) ASMJIT_NOTHROW;
+ MemNode* findPtr(uint8_t* mem) ASMJIT_NOTHROW;
+
+ // --------------------------------------------------------------------------
+ // [Members]
+ // --------------------------------------------------------------------------
+
+#if defined(ASMJIT_WINDOWS)
+ HANDLE _hProcess; // Process where to allocate memory.
+#endif // ASMJIT_WINDOWS
+ Lock _lock; // Lock for thread safety.
+
+ sysuint_t _newChunkSize; // Default node size.
+ sysuint_t _newChunkDensity; // Default node density.
+ sysuint_t _allocated; // How many bytes are allocated.
+ sysuint_t _used; // How many bytes are used.
+
+ // Memory nodes list.
+ MemNode* _first;
+ MemNode* _last;
+ MemNode* _optimal;
+
+ // Memory nodes tree.
+ MemNode* _root;
+
+ // Permanent memory.
+ PermanentNode* _permanent;
+
+ // Whether to keep virtual memory after destroy.
+ bool _keepVirtualMemory;
+};
+
+// ============================================================================
+// [AsmJit::MemoryManagerPrivate - Construction / Destruction]
+// ============================================================================
+
+#if !defined(ASMJIT_WINDOWS)
+MemoryManagerPrivate::MemoryManagerPrivate() ASMJIT_NOTHROW :
+#else
+MemoryManagerPrivate::MemoryManagerPrivate(HANDLE hProcess) ASMJIT_NOTHROW :
+ _hProcess(hProcess),
+#endif
+ _newChunkSize(65536),
+ _newChunkDensity(64),
+ _allocated(0),
+ _used(0),
+ _root(NULL),
+ _first(NULL),
+ _last(NULL),
+ _optimal(NULL),
+ _permanent(NULL),
+ _keepVirtualMemory(false)
+{
+}
+
+MemoryManagerPrivate::~MemoryManagerPrivate() ASMJIT_NOTHROW
+{
+ // Freeable memory cleanup - Also frees the virtual memory if configured to.
+ freeAll(_keepVirtualMemory);
+
+ // Permanent memory cleanup - Never frees the virtual memory.
+ PermanentNode* node = _permanent;
+ while (node)
+ {
+ PermanentNode* prev = node->prev;
+ ASMJIT_FREE(node);
+ node = prev;
+ }
+}
+
+// ============================================================================
+// [AsmJit::MemoryManagerPrivate - Allocation]
+// ============================================================================
+
+// Allocates virtual memory node and MemNode structure.
+//
+// Returns MemNode* on success, otherwise NULL.
+MemNode* MemoryManagerPrivate::createNode(sysuint_t size, sysuint_t density) ASMJIT_NOTHROW
+{
+ sysuint_t vsize;
+ uint8_t* vmem = allocVirtualMemory(size, &vsize);
+
+ // Out of memory.
+ if (vmem == NULL) return NULL;
+
+ sysuint_t blocks = (vsize / density);
+ sysuint_t bsize = (((blocks + 7) >> 3) + sizeof(sysuint_t) - 1) & ~(sysuint_t)(sizeof(sysuint_t)-1);
+
+ MemNode* node = reinterpret_cast<MemNode*>(ASMJIT_MALLOC(sizeof(MemNode)));
+ uint8_t* data = reinterpret_cast<uint8_t*>(ASMJIT_MALLOC(bsize * 2));
+
+ // Out of memory.
+ if (node == NULL || data == NULL)
+ {
+ freeVirtualMemory(vmem, vsize);
+ if (node) ASMJIT_FREE(node);
+ if (data) ASMJIT_FREE(data);
+ return NULL;
+ }
+
+ // Initialize RbNode data.
+ node->node[0] = NULL;
+ node->node[1] = NULL;
+ node->red = 1;
+ node->mem = vmem;
+
+ // Initialize MemNode data.
+ node->prev = NULL;
+ node->next = NULL;
+
+ node->size = vsize;
+ node->blocks = blocks;
+ node->density = density;
+ node->used = 0;
+ node->largestBlock = vsize;
+
+ memset(data, 0, bsize * 2);
+ node->baUsed = reinterpret_cast<sysuint_t*>(data);
+ node->baCont = reinterpret_cast<sysuint_t*>(data + bsize);
+
+ return node;
+}
+
+void* MemoryManagerPrivate::allocPermanent(sysuint_t vsize) ASMJIT_NOTHROW
+{
+ static const sysuint_t permanentAlignment = 32;
+ static const sysuint_t permanentNodeSize = 32768;
+
+ sysuint_t over = vsize % permanentAlignment;
+ if (over) over = permanentAlignment - over;
+ sysuint_t alignedSize = vsize + over;
+
+ AutoLock locked(_lock);
+
+ PermanentNode* node = _permanent;
+
+ // Try to find space in allocated chunks.
+ while (node && alignedSize > node->getAvailable()) node = node->prev;
+
+ // Or allocate new node.
+ if (!node)
+ {
+ sysuint_t nodeSize = permanentNodeSize;
+ if (vsize > nodeSize) nodeSize = vsize;
+
+ node = (PermanentNode*)ASMJIT_MALLOC(sizeof(PermanentNode));
+ // Out of memory.
+ if (node == NULL) return NULL;
+
+ node->mem = allocVirtualMemory(nodeSize, &node->size);
+ // Out of memory.
+ if (node->mem == NULL)
+ {
+ ASMJIT_FREE(node);
+ return NULL;
+ }
+
+ node->used = 0;
+ node->prev = _permanent;
+ _permanent = node;
+ }
+
+ // Finally, copy function code to our space we reserved for.
+ uint8_t* result = node->mem + node->used;
+
+ // Update Statistics.
+ node->used += alignedSize;
+ _used += alignedSize;
+
+ // Code can be null to only reserve space for code.
+ return (void*)result;
+}
+
+void* MemoryManagerPrivate::allocFreeable(sysuint_t vsize) ASMJIT_NOTHROW
+{
+ sysuint_t i; // Current index.
+ sysuint_t need; // How many we need to be freed.
+ sysuint_t minVSize;
+
+ // Align to 32 bytes (our default alignment).
+ vsize = (vsize + 31) & ~(sysuint_t)31;
+ if (vsize == 0) return NULL;
+
+ AutoLock locked(_lock);
+ MemNode* node = _optimal;
+
+ minVSize = _newChunkSize;
+
+ // Try to find memory block in existing nodes.
+ while (node)
+ {
+ // Skip this node?
+ if ((node->getAvailable() < vsize) ||
+ (node->largestBlock < vsize && node->largestBlock != 0))
+ {
+ MemNode* next = node->next;
+ if (node->getAvailable() < minVSize && node == _optimal && next) _optimal = next;
+ node = next;
+ continue;
+ }
+
+ sysuint_t* up = node->baUsed; // Current ubits address.
+ sysuint_t ubits; // Current ubits[0] value.
+ sysuint_t bit; // Current bit mask.
+ sysuint_t blocks = node->blocks; // Count of blocks in node.
+ sysuint_t cont = 0; // How many bits are currently freed in find loop.
+ sysuint_t maxCont = 0; // Largest continuous block (bits count).
+ sysuint_t j;
+
+ need = M_DIV((vsize + node->density - 1), node->density);
+ i = 0;
+
+ // Try to find node that is large enough.
+ while (i < blocks)
+ {
+ ubits = *up++;
+
+ // Fast skip used blocks.
+ if (ubits == (sysuint_t)-1)
+ {
+ if (cont > maxCont) maxCont = cont;
+ cont = 0;
+
+ i += BITS_PER_ENTITY;
+ continue;
+ }
+
+ sysuint_t max = BITS_PER_ENTITY;
+ if (i + max > blocks) max = blocks - i;
+
+ for (j = 0, bit = 1; j < max; bit <<= 1)
+ {
+ j++;
+ if ((ubits & bit) == 0)
+ {
+ if (++cont == need) { i += j; i -= cont; goto found; }
+ continue;
+ }
+
+ if (cont > maxCont) maxCont = cont;
+ cont = 0;
+ }
+
+ i += BITS_PER_ENTITY;
+ }
+
+ // Because we traversed entire node, we can set largest node size that
+ // will be used to cache next traversing..
+ node->largestBlock = maxCont * node->density;
+
+ node = node->next;
+ }
+
+ // If we are here, we failed to find existing memory block and we must
+ // allocate new.
+ {
+ sysuint_t chunkSize = _newChunkSize;
+ if (chunkSize < vsize) chunkSize = vsize;
+
+ node = createNode(chunkSize, _newChunkDensity);
+ if (node == NULL) return NULL;
+
+ // Update binary tree.
+ insertNode(node);
+ ASMJIT_ASSERT(checkTree());
+
+ // Alloc first node at start.
+ i = 0;
+ need = (vsize + node->density - 1) / node->density;
+
+ // Update statistics.
+ _allocated += node->size;
+ }
+
+found:
+ // Update bits.
+ _SetBits(node->baUsed, i, need);
+ _SetBits(node->baCont, i, need - 1);
+
+ // Update statistics.
+ {
+ sysuint_t u = need * node->density;
+ node->used += u;
+ node->largestBlock = 0;
+ _used += u;
+ }
+
+ // And return pointer to allocated memory.
+ uint8_t* result = node->mem + i * node->density;
+ ASMJIT_ASSERT(result >= node->mem && result <= node->mem + node->size - vsize);
+ return result;
+}
+
+bool MemoryManagerPrivate::free(void* address) ASMJIT_NOTHROW
+{
+ if (address == NULL) return true;
+
+ AutoLock locked(_lock);
+
+ MemNode* node = findPtr((uint8_t*)address);
+ if (node == NULL)
+ return false;
+
+ sysuint_t offset = (sysuint_t)((uint8_t*)address - (uint8_t*)node->mem);
+ sysuint_t bitpos = M_DIV(offset, node->density);
+ sysuint_t i = (bitpos / BITS_PER_ENTITY);
+
+ sysuint_t* up = node->baUsed + i; // Current ubits address.
+ sysuint_t* cp = node->baCont + i; // Current cbits address.
+ sysuint_t ubits = *up; // Current ubits[0] value.
+ sysuint_t cbits = *cp; // Current cbits[0] value.
+ sysuint_t bit = (sysuint_t)1 << (bitpos % BITS_PER_ENTITY);
+
+ sysuint_t cont = 0;
+ bool stop;
+
+ for (;;)
+ {
+ stop = (cbits & bit) == 0;
+ ubits &= ~bit;
+ cbits &= ~bit;
+
+ bit <<= 1;
+ cont++;
+
+ if (stop || bit == 0)
+ {
+ *up = ubits;
+ *cp = cbits;
+ if (stop) break;
+
+ ubits = *++up;
+ cbits = *++cp;
+ bit = 1;
+ }
+ }
+
+ // If the freed block is fully allocated node then it's needed to
+ // update 'optimal' pointer in memory manager.
+ if (node->used == node->size)
+ {
+ MemNode* cur = _optimal;
+
+ do {
+ cur = cur->prev;
+ if (cur == node) { _optimal = node; break; }
+ } while (cur);
+ }
+
+ // Statistics.
+ cont *= node->density;
+ if (node->largestBlock < cont) node->largestBlock = cont;
+ node->used -= cont;
+ _used -= cont;
+
+ // If page is empty, we can free it.
+ if (node->used == 0)
+ {
+ // Free memory associated with node (this memory is not accessed
+ // anymore so it's safe).
+ freeVirtualMemory(node->mem, node->size);
+ ASMJIT_FREE(node->baUsed);
+
+ node->baUsed = NULL;
+ node->baCont = NULL;
+
+ // Statistics.
+ _allocated -= node->size;
+
+ // Remove node. This function can return different node than
+ // passed into, but data is copied into previous node if needed.
+ ASMJIT_FREE(removeNode(node));
+ ASMJIT_ASSERT(checkTree());
+ }
+
+ return true;
+}
+
+bool MemoryManagerPrivate::shrink(void* address, sysuint_t used) ASMJIT_NOTHROW
+{
+ if (address == NULL) return false;
+ if (used == 0) return free(address);
+
+ AutoLock locked(_lock);
+
+ MemNode* node = findPtr((uint8_t*)address);
+ if (node == NULL)
+ return false;
+
+ sysuint_t offset = (sysuint_t)((uint8_t*)address - (uint8_t*)node->mem);
+ sysuint_t bitpos = M_DIV(offset, node->density);
+ sysuint_t i = (bitpos / BITS_PER_ENTITY);
+
+ sysuint_t* up = node->baUsed + i; // Current ubits address.
+ sysuint_t* cp = node->baCont + i; // Current cbits address.
+ sysuint_t ubits = *up; // Current ubits[0] value.
+ sysuint_t cbits = *cp; // Current cbits[0] value.
+ sysuint_t bit = (sysuint_t)1 << (bitpos % BITS_PER_ENTITY);
+
+ sysuint_t cont = 0;
+ sysuint_t usedBlocks = (used + node->density - 1) / node->density;
+
+ bool stop;
+
+ // Find the first block we can mark as free.
+ for (;;)
+ {
+ stop = (cbits & bit) == 0;
+ if (stop) return true;
+
+ if (++cont == usedBlocks) break;
+
+ bit <<= 1;
+ if (bit == 0)
+ {
+ ubits = *++up;
+ cbits = *++cp;
+ bit = 1;
+ }
+ }
+
+ // Free the tail blocks.
+ cont = (sysuint_t)-1;
+ goto enterFreeLoop;
+
+ for (;;)
+ {
+ stop = (cbits & bit) == 0;
+ ubits &= ~bit;
+enterFreeLoop:
+ cbits &= ~bit;
+
+ bit <<= 1;
+ cont++;
+
+ if (stop || bit == 0)
+ {
+ *up = ubits;
+ *cp = cbits;
+ if (stop) break;
+
+ ubits = *++up;
+ cbits = *++cp;
+ bit = 1;
+ }
+ }
+
+ // Statistics.
+ cont *= node->density;
+ if (node->largestBlock < cont) node->largestBlock = cont;
+ node->used -= cont;
+ _used -= cont;
+
+ return true;
+}
+
+void MemoryManagerPrivate::freeAll(bool keepVirtualMemory) ASMJIT_NOTHROW
+{
+ MemNode* node = _first;
+
+ while (node)
+ {
+ MemNode* next = node->next;
+
+ if (!keepVirtualMemory) freeVirtualMemory(node->mem, node->size);
+ ASMJIT_FREE(node->baUsed);
+ ASMJIT_FREE(node);
+
+ node = next;
+ }
+
+ _allocated = 0;
+ _used = 0;
+
+ _root = NULL;
+ _first = NULL;
+ _last = NULL;
+ _optimal = NULL;
+}
+
+// ============================================================================
+// [AsmJit::MemoryManagerPrivate - NodeList RB-Tree]
+// ============================================================================
+
+static int rbAssert(MemNode* root)
+{
+ if (root == NULL) return 1;
+
+ MemNode* ln = root->node[0];
+ MemNode* rn = root->node[1];
+
+ // Red violation.
+ ASMJIT_ASSERT( !(isRed(root) && (isRed(ln) || isRed(rn))) );
+
+ int lh = rbAssert(ln);
+ int rh = rbAssert(rn);
+
+ // Invalid btree.
+ ASMJIT_ASSERT(ln == NULL || ln->mem < root->mem);
+ ASMJIT_ASSERT(rn == NULL || rn->mem > root->mem);
+
+ // Black violation.
+ ASMJIT_ASSERT( !(lh != 0 && rh != 0 && lh != rh) );
+
+ // Only count black links.
+ if (lh != 0 && rh != 0)
+ return isRed(root) ? lh : lh + 1;
+ else
+ return 0;
+}
+
+static inline MemNode* rbRotateSingle(MemNode* root, int dir)
+{
+ MemNode* save = root->node[!dir];
+
+ root->node[!dir] = save->node[dir];
+ save->node[dir] = root;
+
+ root->red = 1;
+ save->red = 0;
+
+ return save;
+}
+
+static inline MemNode* rbRotateDouble(MemNode* root, int dir)
+{
+ root->node[!dir] = rbRotateSingle(root->node[!dir], !dir);
+ return rbRotateSingle(root, dir);
+}
+
+bool MemoryManagerPrivate::checkTree() ASMJIT_NOTHROW
+{
+ return rbAssert(_root) > 0;
+}
+
+void MemoryManagerPrivate::insertNode(MemNode* node) ASMJIT_NOTHROW
+{
+ if (_root == NULL)
+ {
+ // Empty tree case.
+ _root = node;
+ }
+ else
+ {
+ // False tree root.
+ RbNode<MemNode> head = {0};
+
+ // Grandparent & parent.
+ MemNode* g = NULL;
+ MemNode* t = reinterpret_cast<MemNode*>(&head);
+
+ // Iterator & parent.
+ MemNode* p = NULL;
+ MemNode* q = t->node[1] = _root;
+
+ int dir = 0, last;
+
+ // Search down the tree.
+ for (;;)
+ {
+ if (q == NULL)
+ {
+ // Insert new node at the bottom.
+ q = node;
+ p->node[dir] = node;
+ }
+ else if (isRed(q->node[0]) && isRed(q->node[1]))
+ {
+ // Color flip.
+ q->red = 1;
+ q->node[0]->red = 0;
+ q->node[1]->red = 0;
+ }
+
+ // Fix red violation.
+ if (isRed(q) && isRed(p))
+ {
+ int dir2 = t->node[1] == g;
+ t->node[dir2] = (q == p->node[last]) ? rbRotateSingle(g, !last) : rbRotateDouble(g, !last);
+ }
+
+ // Stop if found.
+ if (q == node) break;
+
+ last = dir;
+ dir = q->mem < node->mem;
+
+ // Update helpers.
+ if (g != NULL) t = g;
+ g = p;
+ p = q;
+ q = q->node[dir];
+ }
+
+ // Update root.
+ _root = head.node[1];
+ }
+
+ // Make root black.
+ _root->red = 0;
+
+ // Link with others.
+ node->prev = _last;
+
+ if (_first == NULL)
+ {
+ _first = node;
+ _last = node;
+ _optimal = node;
+ }
+ else
+ {
+ node->prev = _last;
+ _last->next = node;
+ _last = node;
+ }
+}
+
+MemNode* MemoryManagerPrivate::removeNode(MemNode* node) ASMJIT_NOTHROW
+{
+ // False tree root.
+ RbNode<MemNode> head = {0};
+
+ // Helpers.
+ MemNode* q = reinterpret_cast<MemNode*>(&head);
+ MemNode* p = NULL;
+ MemNode* g = NULL;
+ // Found item.
+ MemNode* f = NULL;
+ int dir = 1;
+
+ // Set up.
+ q->node[1] = _root;
+
+ // Search and push a red down.
+ while (q->node[dir] != NULL)
+ {
+ int last = dir;
+
+ // Update helpers.
+ g = p;
+ p = q;
+ q = q->node[dir];
+ dir = q->mem < node->mem;
+
+ // Save found node.
+ if (q == node) f = q;
+
+ // Push the red node down.
+ if (!isRed(q) && !isRed(q->node[dir]))
+ {
+ if (isRed(q->node[!dir]))
+ {
+ p = p->node[last] = rbRotateSingle(q, dir);
+ }
+ else if (!isRed(q->node[!dir]))
+ {
+ MemNode* s = p->node[!last];
+
+ if (s != NULL)
+ {
+ if (!isRed(s->node[!last]) && !isRed(s->node[last]))
+ {
+ // Color flip.
+ p->red = 0;
+ s->red = 1;
+ q->red = 1;
+ }
+ else
+ {
+ int dir2 = g->node[1] == p;
+
+ if (isRed(s->node[last]))
+ g->node[dir2] = rbRotateDouble(p, last);
+ else if (isRed(s->node[!last]))
+ g->node[dir2] = rbRotateSingle(p, last);
+
+ // Ensure correct coloring.
+ q->red = g->node[dir2]->red = 1;
+ g->node[dir2]->node[0]->red = 0;
+ g->node[dir2]->node[1]->red = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Replace and remove.
+ ASMJIT_ASSERT(f != NULL);
+ ASMJIT_ASSERT(f != reinterpret_cast<MemNode*>(&head));
+ ASMJIT_ASSERT(q != reinterpret_cast<MemNode*>(&head));
+
+ if (f != q) f->fillData(q);
+ p->node[p->node[1] == q] = q->node[q->node[0] == NULL];
+
+ // Update root and make it black.
+ if ((_root = head.node[1]) != NULL) _root->red = 0;
+
+ // Unlink.
+ MemNode* next = q->next;
+ MemNode* prev = q->prev;
+
+ if (prev) { prev->next = next; } else { _first = next; }
+ if (next) { next->prev = prev; } else { _last = prev; }
+ if (_optimal == q) { _optimal = prev ? prev : next; }
+
+ return q;
+}
+
+MemNode* MemoryManagerPrivate::findPtr(uint8_t* mem) ASMJIT_NOTHROW
+{
+ MemNode* cur = _root;
+ while (cur)
+ {
+ uint8_t* curMem = cur->mem;
+ if (mem < curMem)
+ {
+ // Go left.
+ cur = cur->node[0];
+ continue;
+ }
+ else
+ {
+ uint8_t* curEnd = curMem + cur->size;
+ if (mem >= curEnd)
+ {
+ // Go right.
+ cur = cur->node[1];
+ continue;
+ }
+ else
+ {
+ // Match.
+ break;
+ }
+ }
+ }
+ return cur;
+}
+
+// ============================================================================
+// [AsmJit::MemoryManager]
+// ============================================================================
+
+MemoryManager::MemoryManager() ASMJIT_NOTHROW
+{
+}
+
+MemoryManager::~MemoryManager() ASMJIT_NOTHROW
+{
+}
+
+MemoryManager* MemoryManager::getGlobal() ASMJIT_NOTHROW
+{
+ static VirtualMemoryManager memmgr;
+ return &memmgr;
+}
+
+// ============================================================================
+// [AsmJit::VirtualMemoryManager]
+// ============================================================================
+
+#if !defined(ASMJIT_WINDOWS)
+VirtualMemoryManager::VirtualMemoryManager() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = new(std::nothrow) MemoryManagerPrivate();
+ _d = (void*)d;
+}
+#else
+VirtualMemoryManager::VirtualMemoryManager() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = new(std::nothrow) MemoryManagerPrivate(GetCurrentProcess());
+ _d = (void*)d;
+}
+
+VirtualMemoryManager::VirtualMemoryManager(HANDLE hProcess) ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = new(std::nothrow) MemoryManagerPrivate(hProcess);
+ _d = (void*)d;
+}
+#endif // ASMJIT_WINDOWS
+
+VirtualMemoryManager::~VirtualMemoryManager() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ delete d;
+}
+
+void* VirtualMemoryManager::alloc(sysuint_t size, uint32_t type) ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+
+ if (type == MEMORY_ALLOC_PERMANENT)
+ return d->allocPermanent(size);
+ else
+ return d->allocFreeable(size);
+}
+
+bool VirtualMemoryManager::free(void* address) ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ return d->free(address);
+}
+
+bool VirtualMemoryManager::shrink(void* address, sysuint_t used) ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ return d->shrink(address, used);
+}
+
+void VirtualMemoryManager::freeAll() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+
+ // Calling MemoryManager::freeAll() will never keep allocated memory.
+ return d->freeAll(false);
+}
+
+sysuint_t VirtualMemoryManager::getUsedBytes() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ return d->_used;
+}
+
+sysuint_t VirtualMemoryManager::getAllocatedBytes() ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ return d->_allocated;
+}
+
+bool VirtualMemoryManager::getKeepVirtualMemory() const ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ return d->_keepVirtualMemory;
+}
+
+void VirtualMemoryManager::setKeepVirtualMemory(bool keepVirtualMemory) ASMJIT_NOTHROW
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ d->_keepVirtualMemory = keepVirtualMemory;
+}
+
+// ============================================================================
+// [AsmJit::VirtualMemoryManager - Debug]
+// ============================================================================
+
+#if defined(ASMJIT_MEMORY_MANAGER_DUMP)
+
+struct ASMJIT_HIDDEN GraphVizContext
+{
+ GraphVizContext();
+ ~GraphVizContext();
+
+ bool openFile(const char* fileName);
+ void closeFile();
+
+ void dumpNode(MemNode* node);
+ void connect(MemNode* node, MemNode* other, const char* dst);
+
+ FILE* file;
+};
+
+GraphVizContext::GraphVizContext() :
+ file(NULL)
+{
+}
+
+GraphVizContext::~GraphVizContext()
+{
+ closeFile();
+}
+
+bool GraphVizContext::openFile(const char* fileName)
+{
+ file = fopen(fileName, "w");
+ return file != NULL;
+}
+
+void GraphVizContext::closeFile()
+{
+ if (file) { fclose(file); file = NULL; }
+}
+
+void GraphVizContext::dumpNode(MemNode* node)
+{
+ fprintf(file, " NODE_%p [shape=record, style=filled, color=%s, label=\"<L>|<C>Mem: %p, Used: %d/%d|<R>\"];\n",
+ node,
+ node->red ? "red" : "gray",
+ node->mem, node->used, node->size);
+
+ if (node->node[0]) connect(node, node->node[0], "L");
+ if (node->node[1]) connect(node, node->node[1], "R");
+}
+
+void GraphVizContext::connect(MemNode* node, MemNode* other, const char* dst)
+{
+ dumpNode(other);
+
+ fprintf(file, " NODE_%p:%s -> NODE_%p:C", node, dst, other);
+ if (other->red) fprintf(file, " [style=bold, color=red]");
+ fprintf(file, ";\n");
+}
+
+void VirtualMemoryManager::dump(const char* fileName)
+{
+ MemoryManagerPrivate* d = reinterpret_cast<MemoryManagerPrivate*>(_d);
+ GraphVizContext ctx;
+ if (!ctx.openFile(fileName)) return;
+
+ fprintf(ctx.file, "digraph {\n");
+ if (d->_root) ctx.dumpNode(d->_root);
+ fprintf(ctx.file, "}\n");
+}
+#endif // ASMJIT_MEMORY_MANAGER_DUMP
+
+} // AsmJit namespace
+
+// [Api-End]
+#include "ApiEnd.h"