LCOV - code coverage report
Current view: top level - asmjit - constpool.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 0 110 0.0 %
Date: 2024-10-18 14:00:27 Functions: 0 8 0.0 %

          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 "./constpool.h"
      34             : #include "./utils.h"
      35             : 
      36             : #include <algorithm>
      37             : 
      38             : // [Api-Begin]
      39             : #include "./asmjit_apibegin.h"
      40             : 
      41             : namespace PLMD {
      42             : namespace asmjit {
      43             : 
      44             : // Binary tree code is based on Julienne Walker's "Andersson Binary Trees"
      45             : // article and implementation. However, only three operations are implemented -
      46             : // get, insert and traverse.
      47             : 
      48             : // ============================================================================
      49             : // [asmjit::ConstPool::Tree - Ops]
      50             : // ============================================================================
      51             : 
      52             : //! \internal
      53             : //!
      54             : //! Remove left horizontal links.
      55             : static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* node) noexcept {
      56           0 :   ConstPool::Node* link = node->_link[0];
      57           0 :   uint32_t level = node->_level;
      58             : 
      59           0 :   if (level != 0 && link && link->_level == level) {
      60           0 :     node->_link[0] = link->_link[1];
      61           0 :     link->_link[1] = node;
      62             : 
      63             :     node = link;
      64             :   }
      65             : 
      66             :   return node;
      67             : }
      68             : 
      69             : //! \internal
      70             : //!
      71             : //! Remove consecutive horizontal links.
      72             : static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* node) noexcept {
      73           0 :   ConstPool::Node* link = node->_link[1];
      74           0 :   uint32_t level = node->_level;
      75             : 
      76           0 :   if (level != 0 && link && link->_link[1] && link->_link[1]->_level == level) {
      77           0 :     node->_link[1] = link->_link[0];
      78           0 :     link->_link[0] = node;
      79             : 
      80             :     node = link;
      81           0 :     node->_level++;
      82             :   }
      83             : 
      84             :   return node;
      85             : }
      86             : 
      87           0 : ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept {
      88           0 :   ConstPool::Node* node = _root;
      89           0 :   size_t dataSize = _dataSize;
      90             : 
      91           0 :   while (node) {
      92           0 :     int c = ::memcmp(node->getData(), data, dataSize);
      93           0 :     if (c == 0)
      94             :       return node;
      95           0 :     node = node->_link[c < 0];
      96             :   }
      97             : 
      98             :   return nullptr;
      99             : }
     100             : 
     101           0 : void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept {
     102           0 :   size_t dataSize = _dataSize;
     103           0 :   _length++;
     104             : 
     105           0 :   if (!_root) {
     106           0 :     _root = newNode;
     107           0 :     return;
     108             :   }
     109             : 
     110             :   ConstPool::Node* node = _root;
     111             :   ConstPool::Node* stack[kHeightLimit];
     112             : 
     113             :   unsigned int top = 0;
     114             :   unsigned int dir;
     115             : 
     116             :   // Find a spot and save the stack.
     117             :   for (;;) {
     118           0 :     stack[top++] = node;
     119           0 :     dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0;
     120             : 
     121           0 :     ConstPool::Node* link = node->_link[dir];
     122           0 :     if (!link) break;
     123             : 
     124             :     node = link;
     125             :   }
     126             : 
     127             :   // Link and rebalance.
     128           0 :   node->_link[dir] = newNode;
     129             : 
     130           0 :   while (top > 0) {
     131             :     // Which child?
     132           0 :     node = stack[--top];
     133             : 
     134           0 :     if (top != 0) {
     135           0 :       dir = stack[top - 1]->_link[1] == node;
     136             :     }
     137             : 
     138             :     node = ConstPoolTree_skewNode(node);
     139             :     node = ConstPoolTree_splitNode(node);
     140             : 
     141             :     // Fix the parent.
     142           0 :     if (top != 0)
     143           0 :       stack[top - 1]->_link[dir] = node;
     144             :     else
     145           0 :       _root = node;
     146             :   }
     147             : }
     148             : 
     149             : // ============================================================================
     150             : // [asmjit::ConstPool - Construction / Destruction]
     151             : // ============================================================================
     152             : 
     153           0 : ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); }
     154           0 : ConstPool::~ConstPool() noexcept {}
     155             : 
     156             : // ============================================================================
     157             : // [asmjit::ConstPool - Reset]
     158             : // ============================================================================
     159             : 
     160           0 : void ConstPool::reset(Zone* zone) noexcept {
     161           0 :   _zone = zone;
     162             : 
     163             :   size_t dataSize = 1;
     164           0 :   for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
     165             :     _tree[i].reset();
     166             :     _tree[i].setDataSize(dataSize);
     167           0 :     _gaps[i] = nullptr;
     168           0 :     dataSize <<= 1;
     169             :   }
     170             : 
     171           0 :   _gapPool = nullptr;
     172           0 :   _size = 0;
     173           0 :   _alignment = 0;
     174           0 : }
     175             : 
     176             : // ============================================================================
     177             : // [asmjit::ConstPool - Ops]
     178             : // ============================================================================
     179             : 
     180             : static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept {
     181           0 :   ConstPool::Gap* gap = self->_gapPool;
     182           0 :   if (!gap) return self->_zone->allocT<ConstPool::Gap>();
     183             : 
     184           0 :   self->_gapPool = gap->_next;
     185           0 :   return gap;
     186             : }
     187             : 
     188             : static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self,  ConstPool::Gap* gap) noexcept {
     189           0 :   gap->_next = self->_gapPool;
     190           0 :   self->_gapPool = gap;
     191             : }
     192             : 
     193           0 : static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noexcept {
     194             :   ASMJIT_ASSERT(length > 0);
     195             : 
     196           0 :   while (length > 0) {
     197             :     size_t gapIndex;
     198             :     size_t gapLength;
     199             : 
     200             :       gapIndex = ConstPool::kIndex16;
     201           0 :     if (length >= 16 && Utils::isAligned<size_t>(offset, 16)) {
     202             :       gapLength = 16;
     203             :     }
     204           0 :     else if (length >= 8 && Utils::isAligned<size_t>(offset, 8)) {
     205             :       gapIndex = ConstPool::kIndex8;
     206             :       gapLength = 8;
     207             :     }
     208           0 :     else if (length >= 4 && Utils::isAligned<size_t>(offset, 4)) {
     209             :       gapIndex = ConstPool::kIndex4;
     210             :       gapLength = 4;
     211             :     }
     212           0 :     else if (length >= 2 && Utils::isAligned<size_t>(offset, 2)) {
     213             :       gapIndex = ConstPool::kIndex2;
     214             :       gapLength = 2;
     215             :     }
     216             :     else {
     217             :       gapIndex = ConstPool::kIndex1;
     218             :       gapLength = 1;
     219             :     }
     220             : 
     221             :     // We don't have to check for errors here, if this failed nothing really
     222             :     // happened (just the gap won't be visible) and it will fail again at
     223             :     // place where checking will cause kErrorNoHeapMemory.
     224             :     ConstPool::Gap* gap = ConstPool_allocGap(self);
     225           0 :     if (!gap) return;
     226             : 
     227           0 :     gap->_next = self->_gaps[gapIndex];
     228           0 :     self->_gaps[gapIndex] = gap;
     229             : 
     230           0 :     gap->_offset = offset;
     231           0 :     gap->_length = gapLength;
     232             : 
     233           0 :     offset += gapLength;
     234           0 :     length -= gapLength;
     235             :   }
     236             : }
     237             : 
     238           0 : Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept {
     239             :   size_t treeIndex;
     240             : 
     241           0 :   if (size == 32)
     242             :     treeIndex = kIndex32;
     243             :   else if (size == 16)
     244             :     treeIndex = kIndex16;
     245             :   else if (size == 8)
     246             :     treeIndex = kIndex8;
     247             :   else if (size == 4)
     248             :     treeIndex = kIndex4;
     249             :   else if (size == 2)
     250             :     treeIndex = kIndex2;
     251             :   else if (size == 1)
     252             :     treeIndex = kIndex1;
     253             :   else
     254             :     return DebugUtils::errored(kErrorInvalidArgument);
     255             : 
     256           0 :   ConstPool::Node* node = _tree[treeIndex].get(data);
     257           0 :   if (node) {
     258           0 :     dstOffset = node->_offset;
     259           0 :     return kErrorOk;
     260             :   }
     261             : 
     262             :   // Before incrementing the current offset try if there is a gap that can
     263             :   // be used for the requested data.
     264             :   size_t offset = ~static_cast<size_t>(0);
     265             :   size_t gapIndex = treeIndex;
     266             : 
     267           0 :   while (gapIndex != kIndexCount - 1) {
     268           0 :     ConstPool::Gap* gap = _gaps[treeIndex];
     269             : 
     270             :     // Check if there is a gap.
     271           0 :     if (gap) {
     272           0 :       size_t gapOffset = gap->_offset;
     273           0 :       size_t gapLength = gap->_length;
     274             : 
     275             :       // Destroy the gap for now.
     276           0 :       _gaps[treeIndex] = gap->_next;
     277             :       ConstPool_freeGap(this, gap);
     278             : 
     279             :       offset = gapOffset;
     280             :       ASMJIT_ASSERT(Utils::isAligned<size_t>(offset, size));
     281             : 
     282           0 :       gapLength -= size;
     283           0 :       if (gapLength > 0)
     284           0 :         ConstPool_addGap(this, gapOffset, gapLength);
     285             :     }
     286             : 
     287           0 :     gapIndex++;
     288             :   }
     289             : 
     290           0 :   if (offset == ~static_cast<size_t>(0)) {
     291             :     // Get how many bytes have to be skipped so the address is aligned accordingly
     292             :     // to the 'size'.
     293           0 :     size_t diff = Utils::alignDiff<size_t>(_size, size);
     294             : 
     295           0 :     if (diff != 0) {
     296           0 :       ConstPool_addGap(this, _size, diff);
     297           0 :       _size += diff;
     298             :     }
     299             : 
     300           0 :     offset = _size;
     301           0 :     _size += size;
     302             :   }
     303             : 
     304             :   // Add the initial node to the right index.
     305           0 :   node = ConstPool::Tree::_newNode(_zone, data, size, offset, false);
     306           0 :   if (!node) return DebugUtils::errored(kErrorNoHeapMemory);
     307             : 
     308           0 :   _tree[treeIndex].put(node);
     309           0 :   _alignment = std::max<size_t>(_alignment, size);
     310             : 
     311           0 :   dstOffset = offset;
     312             : 
     313             :   // Now create a bunch of shared constants that are based on the data pattern.
     314             :   // We stop at size 4, it probably doesn't make sense to split constants down
     315             :   // to 1 byte.
     316             :   size_t pCount = 1;
     317           0 :   while (size > 4) {
     318           0 :     size >>= 1;
     319           0 :     pCount <<= 1;
     320             : 
     321             :     ASMJIT_ASSERT(treeIndex != 0);
     322           0 :     treeIndex--;
     323             : 
     324             :     const uint8_t* pData = static_cast<const uint8_t*>(data);
     325           0 :     for (size_t i = 0; i < pCount; i++, pData += size) {
     326           0 :       node = _tree[treeIndex].get(pData);
     327           0 :       if (node) continue;
     328             : 
     329           0 :       node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true);
     330           0 :       _tree[treeIndex].put(node);
     331             :     }
     332             :   }
     333             : 
     334             :   return kErrorOk;
     335             : }
     336             : 
     337             : // ============================================================================
     338             : // [asmjit::ConstPool - Reset]
     339             : // ============================================================================
     340             : 
     341             : struct ConstPoolFill {
     342             :   ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept :
     343             :     _dst(dst),
     344             :     _dataSize(dataSize) {}
     345             : 
     346             :   ASMJIT_INLINE void visit(const ConstPool::Node* node) noexcept {
     347           0 :     if (!node->_shared)
     348           0 :       ::memcpy(_dst + node->_offset, node->getData(), _dataSize);
     349             :   }
     350             : 
     351             :   uint8_t* _dst;
     352             :   size_t _dataSize;
     353             : };
     354             : 
     355           0 : void ConstPool::fill(void* dst) const noexcept {
     356             :   // Clears possible gaps, asmjit should never emit garbage to the output.
     357           0 :   ::memset(dst, 0, _size);
     358             : 
     359             :   ConstPoolFill filler(static_cast<uint8_t*>(dst), 1);
     360           0 :   for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
     361             :     _tree[i].iterate(filler);
     362           0 :     filler._dataSize <<= 1;
     363             :   }
     364           0 : }
     365             : 
     366             : // ============================================================================
     367             : // [asmjit::ConstPool - Test]
     368             : // ============================================================================
     369             : 
     370             : #if defined(ASMJIT_TEST)
     371             : UNIT(base_constpool) {
     372             :   Zone zone(32384 - Zone::kZoneOverhead);
     373             :   ConstPool pool(&zone);
     374             : 
     375             :   uint32_t i;
     376             :   uint32_t kCount = 1000000;
     377             : 
     378             :   INFO("Adding %u constants to the pool.", kCount);
     379             :   {
     380             :     size_t prevOffset;
     381             :     size_t curOffset;
     382             :     uint64_t c = ASMJIT_UINT64_C(0x0101010101010101);
     383             : 
     384             :     EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk,
     385             :       "pool.add() - Returned error");
     386             :     EXPECT(prevOffset == 0,
     387             :       "pool.add() - First constant should have zero offset");
     388             : 
     389             :     for (i = 1; i < kCount; i++) {
     390             :       c++;
     391             :       EXPECT(pool.add(&c, 8, curOffset) == kErrorOk,
     392             :         "pool.add() - Returned error");
     393             :       EXPECT(prevOffset + 8 == curOffset,
     394             :         "pool.add() - Returned incorrect curOffset");
     395             :       EXPECT(pool.getSize() == (i + 1) * 8,
     396             :         "pool.getSize() - Reported incorrect size");
     397             :       prevOffset = curOffset;
     398             :     }
     399             : 
     400             :     EXPECT(pool.getAlignment() == 8,
     401             :       "pool.getAlignment() - Expected 8-byte alignment");
     402             :   }
     403             : 
     404             :   INFO("Retrieving %u constants from the pool.", kCount);
     405             :   {
     406             :     uint64_t c = ASMJIT_UINT64_C(0x0101010101010101);
     407             : 
     408             :     for (i = 0; i < kCount; i++) {
     409             :       size_t offset;
     410             :       EXPECT(pool.add(&c, 8, offset) == kErrorOk,
     411             :         "pool.add() - Returned error");
     412             :       EXPECT(offset == i * 8,
     413             :         "pool.add() - Should have reused constant");
     414             :       c++;
     415             :     }
     416             :   }
     417             : 
     418             :   INFO("Checking if the constants were split into 4-byte patterns");
     419             :   {
     420             :     uint32_t c = 0x01010101;
     421             :     for (i = 0; i < kCount; i++) {
     422             :       size_t offset;
     423             :       EXPECT(pool.add(&c, 4, offset) == kErrorOk,
     424             :         "pool.add() - Returned error");
     425             :       EXPECT(offset == i * 8,
     426             :         "pool.add() - Should reuse existing constant");
     427             :       c++;
     428             :     }
     429             :   }
     430             : 
     431             :   INFO("Adding 2 byte constant to misalign the current offset");
     432             :   {
     433             :     uint16_t c = 0xFFFF;
     434             :     size_t offset;
     435             : 
     436             :     EXPECT(pool.add(&c, 2, offset) == kErrorOk,
     437             :       "pool.add() - Returned error");
     438             :     EXPECT(offset == kCount * 8,
     439             :       "pool.add() - Didn't return expected position");
     440             :     EXPECT(pool.getAlignment() == 8,
     441             :       "pool.getAlignment() - Expected 8-byte alignment");
     442             :   }
     443             : 
     444             :   INFO("Adding 8 byte constant to check if pool gets aligned again");
     445             :   {
     446             :     uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF);
     447             :     size_t offset;
     448             : 
     449             :     EXPECT(pool.add(&c, 8, offset) == kErrorOk,
     450             :       "pool.add() - Returned error");
     451             :     EXPECT(offset == kCount * 8 + 8,
     452             :       "pool.add() - Didn't return aligned offset");
     453             :   }
     454             : 
     455             :   INFO("Adding 2 byte constant to verify the gap is filled");
     456             :   {
     457             :     uint16_t c = 0xFFFE;
     458             :     size_t offset;
     459             : 
     460             :     EXPECT(pool.add(&c, 2, offset) == kErrorOk,
     461             :       "pool.add() - Returned error");
     462             :     EXPECT(offset == kCount * 8 + 2,
     463             :       "pool.add() - Didn't fill the gap");
     464             :     EXPECT(pool.getAlignment() == 8,
     465             :       "pool.getAlignment() - Expected 8-byte alignment");
     466             :   }
     467             : 
     468             :   INFO("Checking reset functionality");
     469             :   {
     470             :     pool.reset(&zone);
     471             :     zone.reset();
     472             : 
     473             :     EXPECT(pool.getSize() == 0,
     474             :       "pool.getSize() - Expected pool size to be zero");
     475             :     EXPECT(pool.getAlignment() == 0,
     476             :       "pool.getSize() - Expected pool alignment to be zero");
     477             :   }
     478             : 
     479             :   INFO("Checking pool alignment when combined constants are added");
     480             :   {
     481             :     uint8_t bytes[32] = { 0 };
     482             :     size_t offset;
     483             : 
     484             :     pool.add(bytes, 1, offset);
     485             : 
     486             :     EXPECT(pool.getSize() == 1,
     487             :       "pool.getSize() - Expected pool size to be 1 byte");
     488             :     EXPECT(pool.getAlignment() == 1,
     489             :       "pool.getSize() - Expected pool alignment to be 1 byte");
     490             :     EXPECT(offset == 0,
     491             :       "pool.getSize() - Expected offset returned to be zero");
     492             : 
     493             :     pool.add(bytes, 2, offset);
     494             : 
     495             :     EXPECT(pool.getSize() == 4,
     496             :       "pool.getSize() - Expected pool size to be 4 bytes");
     497             :     EXPECT(pool.getAlignment() == 2,
     498             :       "pool.getSize() - Expected pool alignment to be 2 bytes");
     499             :     EXPECT(offset == 2,
     500             :       "pool.getSize() - Expected offset returned to be 2");
     501             : 
     502             :     pool.add(bytes, 4, offset);
     503             : 
     504             :     EXPECT(pool.getSize() == 8,
     505             :       "pool.getSize() - Expected pool size to be 8 bytes");
     506             :     EXPECT(pool.getAlignment() == 4,
     507             :       "pool.getSize() - Expected pool alignment to be 4 bytes");
     508             :     EXPECT(offset == 4,
     509             :       "pool.getSize() - Expected offset returned to be 4");
     510             : 
     511             :     pool.add(bytes, 4, offset);
     512             : 
     513             :     EXPECT(pool.getSize() == 8,
     514             :       "pool.getSize() - Expected pool size to be 8 bytes");
     515             :     EXPECT(pool.getAlignment() == 4,
     516             :       "pool.getSize() - Expected pool alignment to be 4 bytes");
     517             :     EXPECT(offset == 4,
     518             :       "pool.getSize() - Expected offset returned to be 8");
     519             : 
     520             :     pool.add(bytes, 32, offset);
     521             :     EXPECT(pool.getSize() == 64,
     522             :       "pool.getSize() - Expected pool size to be 64 bytes");
     523             :     EXPECT(pool.getAlignment() == 32,
     524             :       "pool.getSize() - Expected pool alignment to be 32 bytes");
     525             :     EXPECT(offset == 32,
     526             :       "pool.getSize() - Expected offset returned to be 32");
     527             :   }
     528             : }
     529             : #endif // ASMJIT_TEST
     530             : 
     531             : } // asmjit namespace
     532             : } // namespace PLMD
     533             : 
     534             : // [Api-End]
     535             : #include "./asmjit_apiend.h"
     536             : #pragma GCC diagnostic pop
     537             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16