LCOV - code coverage report
Current view: top level - asmjit - vmem.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 96 318 30.2 %
Date: 2024-10-11 08:09:49 Functions: 8 14 57.1 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             : Copyright (c) 2008-2017, Petr Kobalicek
       3             : 
       4             : This software is provided 'as-is', without any express or implied
       5             : warranty. In no event will the authors be held liable for any damages
       6             : arising from the use of this software.
       7             : 
       8             : Permission is granted to anyone to use this software for any purpose,
       9             : including commercial applications, and to alter it and redistribute it
      10             : freely, subject to the following restrictions:
      11             : 
      12             : 1. The origin of this software must not be misrepresented; you must not
      13             :    claim that you wrote the original software. If you use this software
      14             :    in a product, an acknowledgment in the product documentation would be
      15             :    appreciated but is not required.
      16             : 2. Altered source versions must be plainly marked as such, and must not be
      17             :    misrepresented as being the original software.
      18             : 3. This notice may not be removed or altered from any source distribution.
      19             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      20             : #ifdef __PLUMED_HAS_ASMJIT
      21             : #pragma GCC diagnostic push
      22             : #pragma GCC diagnostic ignored "-Wpedantic"
      23             : // [AsmJit]
      24             : // Complete x86/x64 JIT and Remote Assembler for C++.
      25             : //
      26             : // [License]
      27             : // Zlib - See LICENSE.md file in the package.
      28             : 
      29             : // [Export]
      30             : #define ASMJIT_EXPORTS
      31             : 
      32             : // [Dependencies]
      33             : #include "./osutils.h"
      34             : #include "./utils.h"
      35             : #include "./vmem.h"
      36             : 
      37             : // [Api-Begin]
      38             : #include "./asmjit_apibegin.h"
      39             : 
      40             : // This file contains implementation of virtual memory management for AsmJit
      41             : // library. There are several goals I decided to write implementation myself:
      42             : //
      43             : // - Granularity of allocated blocks is different than granularity for a typical
      44             : //   C malloc. It is at least 64-bytes so CodeEmitter can guarantee the alignment
      45             : //   up to 64 bytes, which is the size of a cache-line and it's also required by
      46             : //   AVX-512 aligned loads and stores. Alignment requirements can grow in the future,
      47             : //   but at the moment 64 bytes is safe (we may jump to 128 bytes if necessary or
      48             : //   make it configurable).
      49             : //
      50             : // - Keep memory manager information outside of the allocated virtual memory
      51             : //   pages, because these pages allow machine code execution and there should
      52             : //   be not data required to keep track of these blocks. Another reason is that
      53             : //   some environments (i.e. iOS) allow to generate and run JIT code, but this
      54             : //   code has to be set to [Executable, but not Writable].
      55             : //
      56             : // - Keep implementation simple and easy to follow.
      57             : //
      58             : // Implementation is based on bit arrays and binary trees. Bit arrays contain
      59             : // information related to allocated and unused blocks of memory. The size of
      60             : // a block is described by `MemNode::density`. Count of blocks is stored in
      61             : // `MemNode::blocks`. For example if density is 64 and count of blocks is 20,
      62             : // memory node contains 64*20 bytes of memory and the smallest possible allocation
      63             : // (and also alignment) is 64 bytes. So density is also related to memory
      64             : // alignment. Binary trees (RB) are used to enable fast lookup into all addresses
      65             : // allocated by memory manager instance. This is used mainly by `VMemPrivate::release()`.
      66             : //
      67             : // Bit array looks like this (empty = unused, X = used) - Size of block 64:
      68             : //
      69             : //   -------------------------------------------------------------------------
      70             : //   | |X|X| | | | | |X|X|X|X|X|X| | | | | | | | | | | | |X| | | | |X|X|X| | |
      71             : //   -------------------------------------------------------------------------
      72             : //                               (Maximum continuous block)
      73             : //
      74             : // These bits show that there are 12 allocated blocks (X) of 64 bytes, so total
      75             : // size allocated is 768 bytes. Maximum count of continuous memory is 12 * 64.
      76             : 
      77             : namespace PLMD {
      78             : namespace asmjit {
      79             : 
      80             : // ============================================================================
      81             : // [asmjit::VMemMgr - BitOps]
      82             : // ============================================================================
      83             : 
      84             : #define M_DIV(x, y) ((x) / (y))
      85             : #define M_MOD(x, y) ((x) % (y))
      86             : 
      87             : //! \internal
      88             : enum { kBitsPerEntity = (sizeof(size_t) * 8) };
      89             : 
      90             : //! \internal
      91             : //!
      92             : //! Set `len` bits in `buf` starting at `index` bit index.
      93        3888 : static void _SetBits(size_t* buf, size_t index, size_t len) noexcept {
      94        3888 :   if (len == 0)
      95             :     return;
      96             : 
      97        3146 :   size_t i = index / kBitsPerEntity; // size_t[]
      98        3146 :   size_t j = index % kBitsPerEntity; // size_t[][] bit index
      99             : 
     100             :   // How many bytes process in the first group.
     101        3146 :   size_t c = kBitsPerEntity - j;
     102             :   if (c > len)
     103             :     c = len;
     104             : 
     105             :   // Offset.
     106        3146 :   buf += i;
     107             : 
     108        3146 :   *buf++ |= ((~(size_t)0) >> (kBitsPerEntity - c)) << j;
     109        3146 :   len -= c;
     110             : 
     111        3146 :   while (len >= kBitsPerEntity) {
     112           0 :     *buf++ = ~(size_t)0;
     113           0 :     len -= kBitsPerEntity;
     114             :   }
     115             : 
     116        3146 :   if (len)
     117           0 :     *buf |= ((~(size_t)0) >> (kBitsPerEntity - len));
     118             : }
     119             : 
     120             : // ============================================================================
     121             : // [asmjit::VMemMgr::TypeDefs]
     122             : // ============================================================================
     123             : 
     124             : typedef VMemMgr::RbNode RbNode;
     125             : typedef VMemMgr::MemNode MemNode;
     126             : typedef VMemMgr::PermanentNode PermanentNode;
     127             : 
     128             : // ============================================================================
     129             : // [asmjit::VMemMgr::RbNode]
     130             : // ============================================================================
     131             : 
     132             : //! \internal
     133             : //!
     134             : //! Base red-black tree node.
     135             : struct VMemMgr::RbNode {
     136             :   // Implementation is based on article by Julienne Walker (Public Domain),
     137             :   // including C code and original comments. Thanks for the excellent article.
     138             : 
     139             :   RbNode* node[2];                       //!< Left[0] and right[1] nodes.
     140             :   uint8_t* mem;                          //!< Virtual memory address.
     141             :   uint32_t red;                          //!< Node color (red vs. black).
     142             : };
     143             : 
     144             : //! \internal
     145             : //!
     146             : //! Get if the node is red (nullptr or node with red flag).
     147             : static ASMJIT_INLINE bool rbIsRed(RbNode* node) noexcept {
     148           0 :   return node && node->red;
     149             : }
     150             : 
     151             : //! \internal
     152             : //!
     153             : //! Check whether the RB tree is valid.
     154             : static int rbAssert(RbNode* root) noexcept {
     155             :   if (!root) return 1;
     156             : 
     157             :   RbNode* ln = root->node[0];
     158             :   RbNode* rn = root->node[1];
     159             : 
     160             :   // Red violation.
     161             :   ASMJIT_ASSERT(!(rbIsRed(root) && (rbIsRed(ln) || rbIsRed(rn))));
     162             : 
     163             :   int lh = rbAssert(ln);
     164             :   int rh = rbAssert(rn);
     165             : 
     166             :   // Invalid btree.
     167             :   ASMJIT_ASSERT(ln == nullptr || ln->mem < root->mem);
     168             :   ASMJIT_ASSERT(rn == nullptr || rn->mem > root->mem);
     169             : 
     170             :   // Black violation.
     171             :   ASMJIT_ASSERT(!(lh != 0 && rh != 0 && lh != rh));
     172             : 
     173             :   // Only count black links.
     174             :   if (lh != 0 && rh != 0)
     175             :     return rbIsRed(root) ? lh : lh + 1;
     176             :   else
     177             :     return 0;
     178             : }
     179             : 
     180             : //! \internal
     181             : //!
     182             : //! Single rotation.
     183             : static ASMJIT_INLINE RbNode* rbRotateSingle(RbNode* root, int dir) noexcept {
     184           0 :   RbNode* save = root->node[!dir];
     185             : 
     186           0 :   root->node[!dir] = save->node[dir];
     187           0 :   save->node[dir] = root;
     188             : 
     189           0 :   root->red = 1;
     190           0 :   save->red = 0;
     191             : 
     192             :   return save;
     193             : }
     194             : 
     195             : //! \internal
     196             : //!
     197             : //! Double rotation.
     198             : static ASMJIT_INLINE RbNode* rbRotateDouble(RbNode* root, int dir) noexcept {
     199           0 :   root->node[!dir] = rbRotateSingle(root->node[!dir], !dir);
     200             :   return rbRotateSingle(root, dir);
     201             : }
     202             : 
     203             : // ============================================================================
     204             : // [asmjit::VMemMgr::MemNode]
     205             : // ============================================================================
     206             : 
     207             : struct VMemMgr::MemNode : public RbNode {
     208             :   ASMJIT_INLINE void init(MemNode* other) noexcept {
     209           0 :     mem = other->mem;
     210             : 
     211           0 :     size = other->size;
     212           0 :     used = other->used;
     213           0 :     blocks = other->blocks;
     214           0 :     density = other->density;
     215           0 :     largestBlock = other->largestBlock;
     216             : 
     217           0 :     baUsed = other->baUsed;
     218           0 :     baCont = other->baCont;
     219           0 :   }
     220             : 
     221             :   // Get available space.
     222           0 :   ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; }
     223             : 
     224             :   MemNode* prev;         // Prev node in list.
     225             :   MemNode* next;         // Next node in list.
     226             : 
     227             :   size_t size;           // How many bytes contain this node.
     228             :   size_t used;           // How many bytes are used in this node.
     229             :   size_t blocks;         // How many blocks are here.
     230             :   size_t density;        // Minimum count of allocated bytes in this node (also alignment).
     231             :   size_t largestBlock;   // Contains largest block that can be allocated.
     232             : 
     233             :   size_t* baUsed;        // Contains bits about used blocks       (0 = unused, 1 = used).
     234             :   size_t* baCont;        // Contains bits about continuous blocks (0 = stop  , 1 = continue).
     235             : };
     236             : 
     237             : // ============================================================================
     238             : // [asmjit::VMemMgr::PermanentNode]
     239             : // ============================================================================
     240             : 
     241             : //! \internal
     242             : //!
     243             : //! Permanent node.
     244             : struct VMemMgr::PermanentNode {
     245             :   //! Get available space.
     246           0 :   ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; }
     247             : 
     248             :   PermanentNode* prev;   // Pointer to prev chunk or nullptr.
     249             :   uint8_t* mem;          // Base pointer (virtual memory address).
     250             :   size_t size;           // Count of bytes allocated.
     251             :   size_t used;           // Count of bytes used.
     252             : };
     253             : 
     254             : // ============================================================================
     255             : // [asmjit::VMemMgr - Private]
     256             : // ============================================================================
     257             : 
     258             : //! \internal
     259             : //!
     260             : //! Helper to avoid `#ifdef`s in the code.
     261             : ASMJIT_INLINE uint8_t* vMemMgrAllocVMem(VMemMgr* self, size_t size, size_t* vSize) noexcept {
     262             :   uint32_t flags = OSUtils::kVMWritable | OSUtils::kVMExecutable;
     263             : #if !ASMJIT_OS_WINDOWS
     264        1944 :   return static_cast<uint8_t*>(OSUtils::allocVirtualMemory(size, vSize, flags));
     265             : #else
     266             :   return static_cast<uint8_t*>(OSUtils::allocProcessMemory(self->_hProcess, size, vSize, flags));
     267             : #endif
     268             : }
     269             : 
     270             : //! \internal
     271             : //!
     272             : //! Helper to avoid `#ifdef`s in the code.
     273             : ASMJIT_INLINE Error vMemMgrReleaseVMem(VMemMgr* self, void* p, size_t vSize) noexcept {
     274             : #if !ASMJIT_OS_WINDOWS
     275           0 :   return OSUtils::releaseVirtualMemory(p, vSize);
     276             : #else
     277             :   return OSUtils::releaseProcessMemory(self->_hProcess, p, vSize);
     278             : #endif
     279             : }
     280             : 
     281             : //! \internal
     282             : //!
     283             : //! Check whether the Red-Black tree is valid.
     284             : static bool vMemMgrCheckTree(VMemMgr* self) noexcept {
     285             :   return rbAssert(self->_root) > 0;
     286             : }
     287             : 
     288             : //! \internal
     289             : //!
     290             : //! Alloc virtual memory including a heap memory needed for `MemNode` data.
     291             : //!
     292             : //! Returns set-up `MemNode*` or nullptr if allocation failed.
     293        1944 : static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) noexcept {
     294             :   size_t vSize;
     295             :   uint8_t* vmem = vMemMgrAllocVMem(self, size, &vSize);
     296        1944 :   if (!vmem) return nullptr;
     297             : 
     298        1944 :   size_t blocks = (vSize / density);
     299        1944 :   size_t bsize = (((blocks + 7) >> 3) + sizeof(size_t) - 1) & ~(size_t)(sizeof(size_t) - 1);
     300             : 
     301             :   MemNode* node = static_cast<MemNode*>(Internal::allocMemory(sizeof(MemNode)));
     302        1944 :   uint8_t* data = static_cast<uint8_t*>(Internal::allocMemory(bsize * 2));
     303             : 
     304             :   // Out of memory.
     305        1944 :   if (!node || !data) {
     306             :     vMemMgrReleaseVMem(self, vmem, vSize);
     307           0 :     if (node) Internal::releaseMemory(node);
     308           0 :     if (data) Internal::releaseMemory(data);
     309           0 :     return nullptr;
     310             :   }
     311             : 
     312             :   // Initialize RbNode data.
     313        1944 :   node->node[0] = nullptr;
     314        1944 :   node->node[1] = nullptr;
     315        1944 :   node->mem = vmem;
     316        1944 :   node->red = 1;
     317             : 
     318             :   // Initialize MemNode data.
     319        1944 :   node->prev = nullptr;
     320        1944 :   node->next = nullptr;
     321             : 
     322        1944 :   node->size = vSize;
     323        1944 :   node->used = 0;
     324        1944 :   node->blocks = blocks;
     325        1944 :   node->density = density;
     326        1944 :   node->largestBlock = vSize;
     327             : 
     328             :   ::memset(data, 0, bsize * 2);
     329        1944 :   node->baUsed = reinterpret_cast<size_t*>(data);
     330        1944 :   node->baCont = reinterpret_cast<size_t*>(data + bsize);
     331             : 
     332        1944 :   return node;
     333             : }
     334             : 
     335        1944 : static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept {
     336        1944 :   if (!self->_root) {
     337             :     // Empty tree case.
     338        1944 :     self->_root = node;
     339             :   }
     340             :   else {
     341             :     // False tree root.
     342           0 :     RbNode head = { { nullptr, nullptr }, 0, 0 };
     343             : 
     344             :     // Grandparent & parent.
     345             :     RbNode* g = nullptr;
     346             :     RbNode* t = &head;
     347             : 
     348             :     // Iterator & parent.
     349             :     RbNode* p = nullptr;
     350           0 :     RbNode* q = t->node[1] = self->_root;
     351             : 
     352             :     int dir = 0;
     353             :     int last = 0; // Not needed to initialize, but makes some tools happy.
     354             : 
     355             :     // Search down the tree.
     356             :     for (;;) {
     357           0 :       if (!q) {
     358             :         // Insert new node at the bottom.
     359             :         q = node;
     360           0 :         p->node[dir] = node;
     361             :       }
     362           0 :       else if (rbIsRed(q->node[0]) && rbIsRed(q->node[1])) {
     363             :         // Color flip.
     364           0 :         q->red = 1;
     365           0 :         q->node[0]->red = 0;
     366           0 :         q->node[1]->red = 0;
     367             :       }
     368             : 
     369             :       // Fix red violation.
     370             :       if (rbIsRed(q) && rbIsRed(p)) {
     371           0 :         int dir2 = t->node[1] == g;
     372           0 :         t->node[dir2] = q == p->node[last] ? rbRotateSingle(g, !last) : rbRotateDouble(g, !last);
     373             :       }
     374             : 
     375             :       // Stop if found.
     376           0 :       if (q == node)
     377             :         break;
     378             : 
     379             :       last = dir;
     380           0 :       dir = q->mem < node->mem;
     381             : 
     382             :       // Update helpers.
     383           0 :       if (g) t = g;
     384             : 
     385             :       g = p;
     386             :       p = q;
     387           0 :       q = q->node[dir];
     388           0 :     }
     389             : 
     390             :     // Update root.
     391           0 :     self->_root = static_cast<MemNode*>(head.node[1]);
     392             :   }
     393             : 
     394             :   // Make root black.
     395        1944 :   self->_root->red = 0;
     396             : 
     397             :   // Link with others.
     398        1944 :   node->prev = self->_last;
     399             : 
     400        1944 :   if (!self->_first) {
     401        1944 :     self->_first = node;
     402        1944 :     self->_last = node;
     403        1944 :     self->_optimal = node;
     404             :   }
     405             :   else {
     406             :     node->prev = self->_last;
     407           0 :     self->_last->next = node;
     408           0 :     self->_last = node;
     409             :   }
     410        1944 : }
     411             : 
     412             : //! \internal
     413             : //!
     414             : //! Remove node from Red-Black tree.
     415             : //!
     416             : //! Returns node that should be freed, but it doesn't have to be necessarily
     417             : //! the `node` passed.
     418           0 : static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept {
     419             :   // False tree root.
     420           0 :   RbNode head = { { nullptr, nullptr }, 0, 0 };
     421             : 
     422             :   // Helpers.
     423             :   RbNode* q = &head;
     424             :   RbNode* p = nullptr;
     425             :   RbNode* g = nullptr;
     426             : 
     427             :   // Found item.
     428             :   RbNode* f = nullptr;
     429             :   int dir = 1;
     430             : 
     431             :   // Set up.
     432           0 :   q->node[1] = self->_root;
     433             : 
     434             :   // Search and push a red down.
     435           0 :   while (q->node[dir]) {
     436             :     int last = dir;
     437             : 
     438             :     // Update helpers.
     439             :     g = p;
     440             :     p = q;
     441             :     q = q->node[dir];
     442           0 :     dir = q->mem < node->mem;
     443             : 
     444             :     // Save found node.
     445           0 :     if (q == node)
     446             :       f = q;
     447             : 
     448             :     // Push the red node down.
     449           0 :     if (!rbIsRed(q) && !rbIsRed(q->node[dir])) {
     450           0 :       if (rbIsRed(q->node[!dir])) {
     451           0 :         p = p->node[last] = rbRotateSingle(q, dir);
     452             :       }
     453             :       else if (!rbIsRed(q->node[!dir])) {
     454           0 :         RbNode* s = p->node[!last];
     455             : 
     456           0 :         if (s) {
     457           0 :           if (!rbIsRed(s->node[!last]) && !rbIsRed(s->node[last])) {
     458             :             // Color flip.
     459           0 :             p->red = 0;
     460           0 :             s->red = 1;
     461           0 :             q->red = 1;
     462             :           }
     463             :           else {
     464           0 :             int dir2 = g->node[1] == p;
     465             : 
     466           0 :             if (rbIsRed(s->node[last]))
     467           0 :               g->node[dir2] = rbRotateDouble(p, last);
     468             :             else if (rbIsRed(s->node[!last]))
     469           0 :               g->node[dir2] = rbRotateSingle(p, last);
     470             : 
     471             :             // Ensure correct coloring.
     472           0 :             q->red = g->node[dir2]->red = 1;
     473           0 :             g->node[dir2]->node[0]->red = 0;
     474           0 :             g->node[dir2]->node[1]->red = 0;
     475             :           }
     476             :         }
     477             :       }
     478             :     }
     479             :   }
     480             : 
     481             :   // Replace and remove.
     482             :   ASMJIT_ASSERT(f != nullptr);
     483             :   ASMJIT_ASSERT(f != &head);
     484             :   ASMJIT_ASSERT(q != &head);
     485             : 
     486           0 :   if (f != q) {
     487             :     ASMJIT_ASSERT(f != &head);
     488             :     static_cast<MemNode*>(f)->init(static_cast<MemNode*>(q));
     489             :   }
     490             : 
     491           0 :   p->node[p->node[1] == q] = q->node[q->node[0] == nullptr];
     492             : 
     493             :   // Update root and make it black.
     494           0 :   self->_root = static_cast<MemNode*>(head.node[1]);
     495           0 :   if (self->_root) self->_root->red = 0;
     496             : 
     497             :   // Unlink.
     498           0 :   MemNode* next = static_cast<MemNode*>(q)->next;
     499           0 :   MemNode* prev = static_cast<MemNode*>(q)->prev;
     500             : 
     501           0 :   if (prev)
     502           0 :     prev->next = next;
     503             :   else
     504           0 :     self->_first = next;
     505             : 
     506           0 :   if (next)
     507           0 :     next->prev = prev;
     508             :   else
     509           0 :     self->_last  = prev;
     510             : 
     511           0 :   if (self->_optimal == q)
     512           0 :     self->_optimal = prev ? prev : next;
     513             : 
     514           0 :   return static_cast<MemNode*>(q);
     515             : }
     516             : 
     517           0 : static MemNode* vMemMgrFindNodeByPtr(VMemMgr* self, uint8_t* mem) noexcept {
     518           0 :   MemNode* node = self->_root;
     519           0 :   while (node) {
     520           0 :     uint8_t* nodeMem = node->mem;
     521             : 
     522             :     // Go left.
     523           0 :     if (mem < nodeMem) {
     524           0 :       node = static_cast<MemNode*>(node->node[0]);
     525           0 :       continue;
     526             :     }
     527             : 
     528             :     // Go right.
     529           0 :     uint8_t* nodeEnd = nodeMem + node->size;
     530           0 :     if (mem >= nodeEnd) {
     531           0 :       node = static_cast<MemNode*>(node->node[1]);
     532           0 :       continue;
     533             :     }
     534             : 
     535             :     // Match.
     536             :     break;
     537             :   }
     538           0 :   return node;
     539             : }
     540             : 
     541           0 : static void* vMemMgrAllocPermanent(VMemMgr* self, size_t vSize) noexcept {
     542             :   static const size_t permanentAlignment = 32;
     543             :   static const size_t permanentNodeSize  = 32768;
     544             : 
     545             :   vSize = Utils::alignTo<size_t>(vSize, permanentAlignment);
     546             : 
     547             :   AutoLock locked(self->_lock);
     548           0 :   PermanentNode* node = self->_permanent;
     549             : 
     550             :   // Try to find space in allocated chunks.
     551           0 :   while (node && vSize > node->getAvailable())
     552           0 :     node = node->prev;
     553             : 
     554             :   // Or allocate new node.
     555           0 :   if (!node) {
     556             :     size_t nodeSize = permanentNodeSize;
     557             :     if (nodeSize < vSize) nodeSize = vSize;
     558             : 
     559             :     node = static_cast<PermanentNode*>(Internal::allocMemory(sizeof(PermanentNode)));
     560           0 :     if (!node) return nullptr;
     561             : 
     562           0 :     node->mem = vMemMgrAllocVMem(self, nodeSize, &node->size);
     563           0 :     if (!node->mem) {
     564             :       Internal::releaseMemory(node);
     565           0 :       return nullptr;
     566             :     }
     567             : 
     568           0 :     node->used = 0;
     569           0 :     node->prev = self->_permanent;
     570           0 :     self->_permanent = node;
     571             :   }
     572             : 
     573             :   // Finally, copy function code to our space we reserved for.
     574           0 :   uint8_t* result = node->mem + node->used;
     575             : 
     576             :   // Update Statistics.
     577           0 :   node->used += vSize;
     578           0 :   self->_usedBytes += vSize;
     579             : 
     580             :   // Code can be null to only reserve space for code.
     581           0 :   return static_cast<void*>(result);
     582             : }
     583             : 
     584        1944 : static void* vMemMgrAllocFreeable(VMemMgr* self, size_t vSize) noexcept {
     585             :   // Current index.
     586             :   size_t i;
     587             : 
     588             :   // How many we need to be freed.
     589             :   size_t need;
     590             :   size_t minVSize;
     591             : 
     592             :   // Align to 32 bytes by default.
     593             :   vSize = Utils::alignTo<size_t>(vSize, 32);
     594        1944 :   if (vSize == 0)
     595             :     return nullptr;
     596             : 
     597             :   AutoLock locked(self->_lock);
     598        1944 :   MemNode* node = self->_optimal;
     599        1944 :   minVSize = self->_blockSize;
     600             : 
     601             :   // Try to find memory block in existing nodes.
     602        1944 :   while (node) {
     603             :     // Skip this node?
     604           0 :     if ((node->getAvailable() < vSize) || (node->largestBlock < vSize && node->largestBlock != 0)) {
     605           0 :       MemNode* next = node->next;
     606             : 
     607           0 :       if (node->getAvailable() < minVSize && node == self->_optimal && next)
     608           0 :         self->_optimal = next;
     609             : 
     610             :       node = next;
     611           0 :       continue;
     612           0 :     }
     613             : 
     614           0 :     size_t* up = node->baUsed;     // Current ubits address.
     615             :     size_t ubits;                  // Current ubits[0] value.
     616             :     size_t bit;                    // Current bit mask.
     617           0 :     size_t blocks = node->blocks;  // Count of blocks in node.
     618             :     size_t cont = 0;               // How many bits are currently freed in find loop.
     619             :     size_t maxCont = 0;            // Largest continuous block (bits count).
     620             :     size_t j;
     621             : 
     622           0 :     need = M_DIV((vSize + node->density - 1), node->density);
     623             :     i = 0;
     624             : 
     625             :     // Try to find node that is large enough.
     626           0 :     while (i < blocks) {
     627           0 :       ubits = *up++;
     628             : 
     629             :       // Fast skip used blocks.
     630           0 :       if (ubits == ~(size_t)0) {
     631             :         if (cont > maxCont)
     632             :           maxCont = cont;
     633             :         cont = 0;
     634             : 
     635           0 :         i += kBitsPerEntity;
     636           0 :         continue;
     637             :       }
     638             : 
     639             :       size_t max = kBitsPerEntity;
     640           0 :       if (i + max > blocks)
     641           0 :         max = blocks - i;
     642             : 
     643           0 :       for (j = 0, bit = 1; j < max; bit <<= 1) {
     644           0 :         j++;
     645           0 :         if ((ubits & bit) == 0) {
     646           0 :           if (++cont == need) {
     647           0 :             i += j;
     648           0 :             i -= cont;
     649           0 :             goto L_Found;
     650             :           }
     651             : 
     652           0 :           continue;
     653             :         }
     654             : 
     655             :         if (cont > maxCont) maxCont = cont;
     656             :         cont = 0;
     657             :       }
     658             : 
     659             :       i += kBitsPerEntity;
     660             :     }
     661             : 
     662             :     // Because we traversed the entire node, we can set largest node size that
     663             :     // will be used to cache next traversing.
     664           0 :     node->largestBlock = maxCont * node->density;
     665             : 
     666           0 :     node = node->next;
     667             :   }
     668             : 
     669             :   // If we are here, we failed to find existing memory block and we must
     670             :   // allocate a new one.
     671             :   {
     672        1944 :     size_t blockSize = self->_blockSize;
     673             :     if (blockSize < vSize) blockSize = vSize;
     674             : 
     675        1944 :     node = vMemMgrCreateNode(self, blockSize, self->_blockDensity);
     676        1944 :     if (!node) return nullptr;
     677             : 
     678             :     // Update binary tree.
     679        1944 :     vMemMgrInsertNode(self, node);
     680             :     ASMJIT_ASSERT(vMemMgrCheckTree(self));
     681             : 
     682             :     // Alloc first node at start.
     683             :     i = 0;
     684        1944 :     need = (vSize + node->density - 1) / node->density;
     685             : 
     686             :     // Update statistics.
     687        1944 :     self->_allocatedBytes += node->size;
     688             :   }
     689             : 
     690        1944 : L_Found:
     691             :   // Update bits.
     692        1944 :   _SetBits(node->baUsed, i, need);
     693        1944 :   _SetBits(node->baCont, i, need - 1);
     694             : 
     695             :   // Update statistics.
     696             :   {
     697        1944 :     size_t u = need * node->density;
     698        1944 :     node->used += u;
     699        1944 :     node->largestBlock = 0;
     700        1944 :     self->_usedBytes += u;
     701             :   }
     702             : 
     703             :   // And return pointer to allocated memory.
     704        1944 :   uint8_t* result = node->mem + i * node->density;
     705             :   ASMJIT_ASSERT(result >= node->mem && result <= node->mem + node->size - vSize);
     706        1944 :   return result;
     707             : }
     708             : 
     709             : //! \internal
     710             : //!
     711             : //! Reset the whole `VMemMgr` instance, freeing all heap memory allocated an
     712             : //! virtual memory allocated unless `keepVirtualMemory` is true (and this is
     713             : //! only used when writing data to a remote process).
     714        1972 : static void vMemMgrReset(VMemMgr* self, bool keepVirtualMemory) noexcept {
     715        1972 :   MemNode* node = self->_first;
     716             : 
     717        3916 :   while (node) {
     718        1944 :     MemNode* next = node->next;
     719             : 
     720        1944 :     if (!keepVirtualMemory)
     721        1944 :       vMemMgrReleaseVMem(self, node->mem, node->size);
     722             : 
     723        1944 :     Internal::releaseMemory(node->baUsed);
     724             :     Internal::releaseMemory(node);
     725             : 
     726             :     node = next;
     727             :   }
     728             : 
     729        1972 :   self->_allocatedBytes = 0;
     730        1972 :   self->_usedBytes = 0;
     731             : 
     732        1972 :   self->_root = nullptr;
     733        1972 :   self->_first = nullptr;
     734        1972 :   self->_last = nullptr;
     735        1972 :   self->_optimal = nullptr;
     736        1972 : }
     737             : 
     738             : // ============================================================================
     739             : // [asmjit::VMemMgr - Construction / Destruction]
     740             : // ============================================================================
     741             : 
     742             : #if !ASMJIT_OS_WINDOWS
     743        1972 : VMemMgr::VMemMgr() noexcept {
     744             : #else
     745             : VMemMgr::VMemMgr(HANDLE hProcess) noexcept {
     746             : #endif
     747             : 
     748        1972 :   VMemInfo vm = OSUtils::getVirtualMemoryInfo();
     749             : 
     750             : #if ASMJIT_OS_WINDOWS
     751             :   _hProcess = hProcess ? hProcess : vm.hCurrentProcess;
     752             : #endif // ASMJIT_OS_WINDOWS
     753             : 
     754        1972 :   _blockSize = vm.pageGranularity;
     755        1972 :   _blockDensity = 64;
     756             : 
     757        1972 :   _allocatedBytes = 0;
     758        1972 :   _usedBytes = 0;
     759             : 
     760        1972 :   _root = nullptr;
     761        1972 :   _first = nullptr;
     762        1972 :   _last = nullptr;
     763        1972 :   _optimal = nullptr;
     764             : 
     765        1972 :   _permanent = nullptr;
     766        1972 :   _keepVirtualMemory = false;
     767        1972 : }
     768             : 
     769        1972 : VMemMgr::~VMemMgr() noexcept {
     770             :   // Freeable memory cleanup - Also frees the virtual memory if configured to.
     771        1972 :   vMemMgrReset(this, _keepVirtualMemory);
     772             : 
     773             :   // Permanent memory cleanup - Never frees the virtual memory.
     774        1972 :   PermanentNode* node = _permanent;
     775        1972 :   while (node) {
     776           0 :     PermanentNode* prev = node->prev;
     777             :     Internal::releaseMemory(node);
     778             :     node = prev;
     779             :   }
     780        1972 : }
     781             : 
     782             : // ============================================================================
     783             : // [asmjit::VMemMgr - Reset]
     784             : // ============================================================================
     785             : 
     786           0 : void VMemMgr::reset() noexcept {
     787           0 :   vMemMgrReset(this, false);
     788           0 : }
     789             : 
     790             : // ============================================================================
     791             : // [asmjit::VMemMgr - Alloc / Release]
     792             : // ============================================================================
     793             : 
     794        1944 : void* VMemMgr::alloc(size_t size, uint32_t type) noexcept {
     795        1944 :   if (type == kAllocPermanent)
     796           0 :     return vMemMgrAllocPermanent(this, size);
     797             :   else
     798        1944 :     return vMemMgrAllocFreeable(this, size);
     799             : }
     800             : 
     801           0 : Error VMemMgr::release(void* p) noexcept {
     802           0 :   if (!p) return kErrorOk;
     803             : 
     804             :   AutoLock locked(_lock);
     805           0 :   MemNode* node = vMemMgrFindNodeByPtr(this, static_cast<uint8_t*>(p));
     806           0 :   if (!node) return DebugUtils::errored(kErrorInvalidArgument);
     807             : 
     808           0 :   size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem);
     809           0 :   size_t bitpos = M_DIV(offset, node->density);
     810           0 :   size_t i = (bitpos / kBitsPerEntity);
     811             : 
     812           0 :   size_t* up = node->baUsed + i;  // Current ubits address.
     813           0 :   size_t* cp = node->baCont + i;  // Current cbits address.
     814           0 :   size_t ubits = *up;             // Current ubits[0] value.
     815           0 :   size_t cbits = *cp;             // Current cbits[0] value.
     816           0 :   size_t bit = (size_t)1 << (bitpos % kBitsPerEntity);
     817             : 
     818             :   size_t cont = 0;
     819             :   bool stop;
     820             : 
     821             :   for (;;) {
     822           0 :     stop = (cbits & bit) == 0;
     823           0 :     ubits &= ~bit;
     824           0 :     cbits &= ~bit;
     825             : 
     826           0 :     bit <<= 1;
     827           0 :     cont++;
     828             : 
     829           0 :     if (stop || bit == 0) {
     830           0 :       *up = ubits;
     831           0 :       *cp = cbits;
     832           0 :       if (stop)
     833             :         break;
     834             : 
     835           0 :       ubits = *++up;
     836           0 :       cbits = *++cp;
     837             :       bit = 1;
     838             :     }
     839             :   }
     840             : 
     841             :   // If the freed block is fully allocated node then it's needed to
     842             :   // update 'optimal' pointer in memory manager.
     843           0 :   if (node->used == node->size) {
     844           0 :     MemNode* cur = _optimal;
     845             : 
     846             :     do {
     847           0 :       cur = cur->prev;
     848           0 :       if (cur == node) {
     849           0 :         _optimal = node;
     850           0 :         break;
     851             :       }
     852           0 :     } while (cur);
     853             :   }
     854             : 
     855             :   // Statistics.
     856           0 :   cont *= node->density;
     857           0 :   if (node->largestBlock < cont)
     858           0 :     node->largestBlock = cont;
     859             : 
     860           0 :   node->used -= cont;
     861           0 :   _usedBytes -= cont;
     862             : 
     863             :   // If page is empty, we can free it.
     864           0 :   if (node->used == 0) {
     865             :     // Free memory associated with node (this memory is not accessed
     866             :     // anymore so it's safe).
     867           0 :     vMemMgrReleaseVMem(this, node->mem, node->size);
     868           0 :     Internal::releaseMemory(node->baUsed);
     869             : 
     870           0 :     node->baUsed = nullptr;
     871           0 :     node->baCont = nullptr;
     872             : 
     873             :     // Statistics.
     874           0 :     _allocatedBytes -= node->size;
     875             : 
     876             :     // Remove node. This function can return different node than
     877             :     // passed into, but data is copied into previous node if needed.
     878           0 :     Internal::releaseMemory(vMemMgrRemoveNode(this, node));
     879             :     ASMJIT_ASSERT(vMemMgrCheckTree(this));
     880             :   }
     881             : 
     882             :   return kErrorOk;
     883             : }
     884             : 
     885           0 : Error VMemMgr::shrink(void* p, size_t used) noexcept {
     886           0 :   if (!p) return kErrorOk;
     887           0 :   if (used == 0)
     888           0 :     return release(p);
     889             : 
     890             :   AutoLock locked(_lock);
     891           0 :   MemNode* node = vMemMgrFindNodeByPtr(this, (uint8_t*)p);
     892           0 :   if (!node) return DebugUtils::errored(kErrorInvalidArgument);
     893             : 
     894           0 :   size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem);
     895           0 :   size_t bitpos = M_DIV(offset, node->density);
     896           0 :   size_t i = (bitpos / kBitsPerEntity);
     897             : 
     898           0 :   size_t* up = node->baUsed + i;  // Current ubits address.
     899           0 :   size_t* cp = node->baCont + i;  // Current cbits address.
     900           0 :   size_t ubits = *up;             // Current ubits[0] value.
     901           0 :   size_t cbits = *cp;             // Current cbits[0] value.
     902           0 :   size_t bit = (size_t)1 << (bitpos % kBitsPerEntity);
     903             : 
     904             :   size_t cont = 0;
     905           0 :   size_t usedBlocks = (used + node->density - 1) / node->density;
     906             : 
     907             :   bool stop;
     908             : 
     909             :   // Find the first block we can mark as free.
     910             :   for (;;) {
     911           0 :     stop = (cbits & bit) == 0;
     912           0 :     if (stop)
     913             :       return kErrorOk;
     914             : 
     915           0 :     if (++cont == usedBlocks)
     916             :       break;
     917             : 
     918           0 :     bit <<= 1;
     919           0 :     if (bit == 0) {
     920           0 :       ubits = *++up;
     921           0 :       cbits = *++cp;
     922             :       bit = 1;
     923             :     }
     924             :   }
     925             : 
     926             :   // Free the tail blocks.
     927             :   cont = ~(size_t)0;
     928           0 :   goto _EnterFreeLoop;
     929             : 
     930             :   for (;;) {
     931           0 :     stop = (cbits & bit) == 0;
     932           0 :     ubits &= ~bit;
     933             : 
     934           0 : _EnterFreeLoop:
     935           0 :     cbits &= ~bit;
     936             : 
     937           0 :     bit <<= 1;
     938           0 :     cont++;
     939             : 
     940           0 :     if (stop || bit == 0) {
     941           0 :       *up = ubits;
     942           0 :       *cp = cbits;
     943           0 :       if (stop)
     944             :         break;
     945             : 
     946           0 :       ubits = *++up;
     947           0 :       cbits = *++cp;
     948             :       bit = 1;
     949             :     }
     950             :   }
     951             : 
     952             :   // Statistics.
     953           0 :   cont *= node->density;
     954           0 :   if (node->largestBlock < cont)
     955           0 :     node->largestBlock = cont;
     956             : 
     957           0 :   node->used -= cont;
     958           0 :   _usedBytes -= cont;
     959             : 
     960           0 :   return kErrorOk;
     961             : }
     962             : 
     963             : // ============================================================================
     964             : // [asmjit::VMem - Test]
     965             : // ============================================================================
     966             : 
     967             : #if defined(ASMJIT_TEST)
     968             : static void VMemTest_fill(void* a, void* b, int i) noexcept {
     969             :   int pattern = rand() % 256;
     970             :   *(int *)a = i;
     971             :   *(int *)b = i;
     972             :   ::memset((char*)a + sizeof(int), pattern, i - sizeof(int));
     973             :   ::memset((char*)b + sizeof(int), pattern, i - sizeof(int));
     974             : }
     975             : 
     976             : static void VMemTest_verify(void* a, void* b) noexcept {
     977             :   int ai = *(int*)a;
     978             :   int bi = *(int*)b;
     979             : 
     980             :   EXPECT(ai == bi,
     981             :     "The length of 'a' (%d) and 'b' (%d) should be same", ai, bi);
     982             : 
     983             :   EXPECT(::memcmp(a, b, ai) == 0,
     984             :     "Pattern (%p) doesn't match", a);
     985             : }
     986             : 
     987             : static void VMemTest_stats(VMemMgr& memmgr) noexcept {
     988             :   INFO("Used     : %u", static_cast<unsigned int>(memmgr.getUsedBytes()));
     989             :   INFO("Allocated: %u", static_cast<unsigned int>(memmgr.getAllocatedBytes()));
     990             : }
     991             : 
     992             : static void VMemTest_shuffle(void** a, void** b, size_t count) noexcept {
     993             :   for (size_t i = 0; i < count; ++i) {
     994             :     size_t si = (size_t)rand() % count;
     995             : 
     996             :     void* ta = a[i];
     997             :     void* tb = b[i];
     998             : 
     999             :     a[i] = a[si];
    1000             :     b[i] = b[si];
    1001             : 
    1002             :     a[si] = ta;
    1003             :     b[si] = tb;
    1004             :   }
    1005             : }
    1006             : 
    1007             : UNIT(base_vmem) {
    1008             :   VMemMgr memmgr;
    1009             : 
    1010             :   // Should be predictible.
    1011             :   srand(100);
    1012             : 
    1013             :   int i;
    1014             :   int kCount = 200000;
    1015             : 
    1016             :   INFO("Memory alloc/free test - %d allocations", static_cast<int>(kCount));
    1017             : 
    1018             :   void** a = (void**)Internal::allocMemory(sizeof(void*) * kCount);
    1019             :   void** b = (void**)Internal::allocMemory(sizeof(void*) * kCount);
    1020             : 
    1021             :   EXPECT(a != nullptr && b != nullptr,
    1022             :     "Couldn't allocate %u bytes on heap", kCount * 2);
    1023             : 
    1024             :   INFO("Allocating virtual memory...");
    1025             :   for (i = 0; i < kCount; i++) {
    1026             :     int r = (rand() % 1000) + 4;
    1027             : 
    1028             :     a[i] = memmgr.alloc(r);
    1029             :     EXPECT(a[i] != nullptr,
    1030             :       "Couldn't allocate %d bytes of virtual memory", r);
    1031             :     ::memset(a[i], 0, r);
    1032             :   }
    1033             :   VMemTest_stats(memmgr);
    1034             : 
    1035             :   INFO("Freeing virtual memory...");
    1036             :   for (i = 0; i < kCount; i++) {
    1037             :     EXPECT(memmgr.release(a[i]) == kErrorOk,
    1038             :       "Failed to free %p", b[i]);
    1039             :   }
    1040             :   VMemTest_stats(memmgr);
    1041             : 
    1042             :   INFO("Verified alloc/free test - %d allocations", static_cast<int>(kCount));
    1043             :   for (i = 0; i < kCount; i++) {
    1044             :     int r = (rand() % 1000) + 4;
    1045             : 
    1046             :     a[i] = memmgr.alloc(r);
    1047             :     EXPECT(a[i] != nullptr,
    1048             :       "Couldn't allocate %d bytes of virtual memory", r);
    1049             : 
    1050             :     b[i] = Internal::allocMemory(r);
    1051             :     EXPECT(b[i] != nullptr,
    1052             :       "Couldn't allocate %d bytes on heap", r);
    1053             : 
    1054             :     VMemTest_fill(a[i], b[i], r);
    1055             :   }
    1056             :   VMemTest_stats(memmgr);
    1057             : 
    1058             :   INFO("Shuffling...");
    1059             :   VMemTest_shuffle(a, b, kCount);
    1060             : 
    1061             :   INFO("Verify and free...");
    1062             :   for (i = 0; i < kCount / 2; i++) {
    1063             :     VMemTest_verify(a[i], b[i]);
    1064             :     EXPECT(memmgr.release(a[i]) == kErrorOk,
    1065             :       "Failed to free %p", a[i]);
    1066             :     Internal::releaseMemory(b[i]);
    1067             :   }
    1068             :   VMemTest_stats(memmgr);
    1069             : 
    1070             :   INFO("Alloc again");
    1071             :   for (i = 0; i < kCount / 2; i++) {
    1072             :     int r = (rand() % 1000) + 4;
    1073             : 
    1074             :     a[i] = memmgr.alloc(r);
    1075             :     EXPECT(a[i] != nullptr,
    1076             :       "Couldn't allocate %d bytes of virtual memory", r);
    1077             : 
    1078             :     b[i] = Internal::allocMemory(r);
    1079             :     EXPECT(b[i] != nullptr,
    1080             :       "Couldn't allocate %d bytes on heap");
    1081             : 
    1082             :     VMemTest_fill(a[i], b[i], r);
    1083             :   }
    1084             :   VMemTest_stats(memmgr);
    1085             : 
    1086             :   INFO("Verify and free...");
    1087             :   for (i = 0; i < kCount; i++) {
    1088             :     VMemTest_verify(a[i], b[i]);
    1089             :     EXPECT(memmgr.release(a[i]) == kErrorOk,
    1090             :       "Failed to free %p", a[i]);
    1091             :     Internal::releaseMemory(b[i]);
    1092             :   }
    1093             :   VMemTest_stats(memmgr);
    1094             : 
    1095             :   Internal::releaseMemory(a);
    1096             :   Internal::releaseMemory(b);
    1097             : }
    1098             : #endif // ASMJIT_TEST
    1099             : 
    1100             : } // asmjit namespace
    1101             : } // namespace PLMD
    1102             : #pragma GCC diagnostic pop
    1103             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.15