LCOV - code coverage report
Current view: top level - asmjit - constpool.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 0 88 0.0 %
Date: 2026-11-05 10:19:06 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #ifdef __PLUMED_HAS_ASMJIT
       2             : // This file is part of AsmJit project <https://asmjit.com>
       3             : //
       4             : // See asmjit.h or LICENSE.md for license and copyright information
       5             : // SPDX-License-Identifier: Zlib
       6             : 
       7             : #include "api-build_p.h"
       8             : #include "constpool.h"
       9             : #include "support.h"
      10             : 
      11             : ASMJIT_BEGIN_NAMESPACE
      12             : 
      13             : // ConstPool - Construction & Destruction
      14             : // ======================================
      15             : 
      16           0 : ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); }
      17           0 : ConstPool::~ConstPool() noexcept {}
      18             : 
      19             : // ConstPool - Reset
      20             : // =================
      21             : 
      22           0 : void ConstPool::reset(Zone* zone) noexcept {
      23           0 :   _zone = zone;
      24             : 
      25             :   size_t dataSize = 1;
      26           0 :   for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
      27           0 :     _tree[i].reset();
      28             :     _tree[i].setDataSize(dataSize);
      29           0 :     _gaps[i] = nullptr;
      30           0 :     dataSize <<= 1;
      31             :   }
      32             : 
      33           0 :   _gapPool = nullptr;
      34           0 :   _size = 0;
      35           0 :   _alignment = 0;
      36           0 :   _minItemSize = 0;
      37           0 : }
      38             : 
      39             : // ConstPool - Operations
      40             : // ======================
      41             : 
      42           0 : static inline ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept {
      43           0 :   ConstPool::Gap* gap = self->_gapPool;
      44           0 :   if (!gap)
      45           0 :     return self->_zone->allocT<ConstPool::Gap>();
      46             : 
      47           0 :   self->_gapPool = gap->_next;
      48           0 :   return gap;
      49             : }
      50             : 
      51             : static inline void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept {
      52           0 :   gap->_next = self->_gapPool;
      53           0 :   self->_gapPool = gap;
      54             : }
      55             : 
      56           0 : static void ConstPool_addGap(ConstPool* self, size_t offset, size_t size) noexcept {
      57             :   ASMJIT_ASSERT(size > 0);
      58             : 
      59           0 :   while (size > 0) {
      60             :     size_t gapIndex;
      61             :     size_t gapSize;
      62             : 
      63           0 :     if (size >= 32 && Support::isAligned<size_t>(offset, 32)) {
      64             :       gapIndex = ConstPool::kIndex32;
      65             :       gapSize = 32;
      66             :     }
      67           0 :     else if (size >= 16 && Support::isAligned<size_t>(offset, 16)) {
      68             :       gapIndex = ConstPool::kIndex16;
      69             :       gapSize = 16;
      70             :     }
      71           0 :     else if (size >= 8 && Support::isAligned<size_t>(offset, 8)) {
      72             :       gapIndex = ConstPool::kIndex8;
      73             :       gapSize = 8;
      74             :     }
      75           0 :     else if (size >= 4 && Support::isAligned<size_t>(offset, 4)) {
      76             :       gapIndex = ConstPool::kIndex4;
      77             :       gapSize = 4;
      78             :     }
      79           0 :     else if (size >= 2 && Support::isAligned<size_t>(offset, 2)) {
      80             :       gapIndex = ConstPool::kIndex2;
      81             :       gapSize = 2;
      82             :     }
      83             :     else {
      84             :       gapIndex = ConstPool::kIndex1;
      85             :       gapSize = 1;
      86             :     }
      87             : 
      88             :     // We don't have to check for errors here, if this failed nothing really happened (just the gap won't be
      89             :     // visible) and it will fail again at place where the same check would generate `kErrorOutOfMemory` error.
      90           0 :     ConstPool::Gap* gap = ConstPool_allocGap(self);
      91           0 :     if (!gap)
      92             :       return;
      93             : 
      94           0 :     gap->_next = self->_gaps[gapIndex];
      95           0 :     self->_gaps[gapIndex] = gap;
      96             : 
      97           0 :     gap->_offset = offset;
      98           0 :     gap->_size = gapSize;
      99             : 
     100           0 :     offset += gapSize;
     101           0 :     size -= gapSize;
     102             :   }
     103             : }
     104             : 
     105           0 : Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept {
     106             :   size_t treeIndex;
     107             : 
     108             :   if (size == 64)
     109             :     treeIndex = kIndex64;
     110             :   else if (size == 32)
     111             :     treeIndex = kIndex32;
     112             :   else if (size == 16)
     113             :     treeIndex = kIndex16;
     114             :   else if (size == 8)
     115             :     treeIndex = kIndex8;
     116             :   else if (size == 4)
     117             :     treeIndex = kIndex4;
     118             :   else if (size == 2)
     119             :     treeIndex = kIndex2;
     120             :   else if (size == 1)
     121             :     treeIndex = kIndex1;
     122             :   else
     123             :     return DebugUtils::errored(kErrorInvalidArgument);
     124             : 
     125           0 :   ConstPool::Node* node = _tree[treeIndex].get(data);
     126           0 :   if (node) {
     127           0 :     dstOffset = node->_offset;
     128           0 :     return kErrorOk;
     129             :   }
     130             : 
     131             :   // Before incrementing the current offset try if there is a gap that can be used for the requested data.
     132             :   size_t offset = ~size_t(0);
     133             :   size_t gapIndex = treeIndex;
     134             : 
     135           0 :   while (gapIndex != kIndexCount - 1) {
     136           0 :     ConstPool::Gap* gap = _gaps[treeIndex];
     137             : 
     138             :     // Check if there is a gap.
     139           0 :     if (gap) {
     140           0 :       size_t gapOffset = gap->_offset;
     141           0 :       size_t gapSize = gap->_size;
     142             : 
     143             :       // Destroy the gap for now.
     144           0 :       _gaps[treeIndex] = gap->_next;
     145             :       ConstPool_freeGap(this, gap);
     146             : 
     147             :       offset = gapOffset;
     148             :       ASMJIT_ASSERT(Support::isAligned<size_t>(offset, size));
     149             : 
     150           0 :       gapSize -= size;
     151           0 :       if (gapSize > 0)
     152           0 :         ConstPool_addGap(this, gapOffset, gapSize);
     153             :     }
     154             : 
     155           0 :     gapIndex++;
     156             :   }
     157             : 
     158           0 :   if (offset == ~size_t(0)) {
     159             :     // Get how many bytes have to be skipped so the address is aligned accordingly to the 'size'.
     160           0 :     size_t diff = Support::alignUpDiff<size_t>(_size, size);
     161             : 
     162           0 :     if (diff != 0) {
     163           0 :       ConstPool_addGap(this, _size, diff);
     164           0 :       _size += diff;
     165             :     }
     166             : 
     167           0 :     offset = _size;
     168           0 :     _size += size;
     169             :   }
     170             : 
     171             :   // Add the initial node to the right index.
     172           0 :   node = ConstPool::Tree::_newNode(_zone, data, size, offset, false);
     173           0 :   if (ASMJIT_UNLIKELY(!node))
     174             :     return DebugUtils::errored(kErrorOutOfMemory);
     175             : 
     176           0 :   _tree[treeIndex].insert(node);
     177           0 :   _alignment = Support::max<size_t>(_alignment, size);
     178             : 
     179           0 :   dstOffset = offset;
     180             : 
     181             :   // Now create a bunch of shared constants that are based on the data pattern. We stop at size 4,
     182             :   // it probably doesn't make sense to split constants down to 1 byte.
     183             :   size_t pCount = 1;
     184             :   size_t smallerSize = size;
     185             : 
     186           0 :   while (smallerSize > 4) {
     187           0 :     pCount <<= 1;
     188           0 :     smallerSize >>= 1;
     189             : 
     190             :     ASMJIT_ASSERT(treeIndex != 0);
     191           0 :     treeIndex--;
     192             : 
     193             :     const uint8_t* pData = static_cast<const uint8_t*>(data);
     194           0 :     for (size_t i = 0; i < pCount; i++, pData += smallerSize) {
     195           0 :       node = _tree[treeIndex].get(pData);
     196           0 :       if (node) continue;
     197             : 
     198           0 :       node = ConstPool::Tree::_newNode(_zone, pData, smallerSize, offset + (i * smallerSize), true);
     199           0 :       _tree[treeIndex].insert(node);
     200             :     }
     201             :   }
     202             : 
     203           0 :   if (_minItemSize == 0)
     204           0 :     _minItemSize = size;
     205             :   else
     206           0 :     _minItemSize = Support::min(_minItemSize, size);
     207             : 
     208             :   return kErrorOk;
     209             : }
     210             : 
     211             : // ConstPool - Reset
     212             : // =================
     213             : 
     214             : struct ConstPoolFill {
     215           0 :   inline ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept :
     216           0 :     _dst(dst),
     217           0 :     _dataSize(dataSize) {}
     218             : 
     219           0 :   inline void operator()(const ConstPool::Node* node) noexcept {
     220           0 :     if (!node->_shared)
     221           0 :       memcpy(_dst + node->_offset, node->data(), _dataSize);
     222           0 :   }
     223             : 
     224             :   uint8_t* _dst;
     225             :   size_t _dataSize;
     226             : };
     227             : 
     228           0 : void ConstPool::fill(void* dst) const noexcept {
     229             :   // Clears possible gaps, asmjit should never emit garbage to the output.
     230           0 :   memset(dst, 0, _size);
     231             : 
     232             :   ConstPoolFill filler(static_cast<uint8_t*>(dst), 1);
     233           0 :   for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
     234           0 :     _tree[i].forEach(filler);
     235           0 :     filler._dataSize <<= 1;
     236             :   }
     237           0 : }
     238             : 
     239             : // ConstPool - Tests
     240             : // =================
     241             : 
     242             : #if defined(ASMJIT_TEST)
     243             : UNIT(const_pool) {
     244             :   Zone zone(32384 - Zone::kBlockOverhead);
     245             :   ConstPool pool(&zone);
     246             : 
     247             :   uint32_t i;
     248             :   uint32_t kCount = BrokenAPI::hasArg("--quick") ? 1000 : 1000000;
     249             : 
     250             :   INFO("Adding %u constants to the pool", kCount);
     251             :   {
     252             :     size_t prevOffset;
     253             :     size_t curOffset;
     254             :     uint64_t c = 0x0101010101010101u;
     255             : 
     256             :     EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk);
     257             :     EXPECT(prevOffset == 0);
     258             : 
     259             :     for (i = 1; i < kCount; i++) {
     260             :       c++;
     261             :       EXPECT(pool.add(&c, 8, curOffset) == kErrorOk);
     262             :       EXPECT(prevOffset + 8 == curOffset);
     263             :       EXPECT(pool.size() == (i + 1) * 8);
     264             :       prevOffset = curOffset;
     265             :     }
     266             : 
     267             :     EXPECT(pool.alignment() == 8);
     268             :   }
     269             : 
     270             :   INFO("Retrieving %u constants from the pool", kCount);
     271             :   {
     272             :     uint64_t c = 0x0101010101010101u;
     273             : 
     274             :     for (i = 0; i < kCount; i++) {
     275             :       size_t offset;
     276             :       EXPECT(pool.add(&c, 8, offset) == kErrorOk);
     277             :       EXPECT(offset == i * 8);
     278             :       c++;
     279             :     }
     280             :   }
     281             : 
     282             :   INFO("Checking if the constants were split into 4-byte patterns");
     283             :   {
     284             :     uint32_t c = 0x01010101;
     285             :     for (i = 0; i < kCount; i++) {
     286             :       size_t offset;
     287             :       EXPECT(pool.add(&c, 4, offset) == kErrorOk);
     288             :       EXPECT(offset == i * 8);
     289             :       c++;
     290             :     }
     291             :   }
     292             : 
     293             :   INFO("Adding 2 byte constant to misalign the current offset");
     294             :   {
     295             :     uint16_t c = 0xFFFF;
     296             :     size_t offset;
     297             : 
     298             :     EXPECT(pool.add(&c, 2, offset) == kErrorOk);
     299             :     EXPECT(offset == kCount * 8);
     300             :     EXPECT(pool.alignment() == 8);
     301             :   }
     302             : 
     303             :   INFO("Adding 8 byte constant to check if pool gets aligned again");
     304             :   {
     305             :     uint64_t c = 0xFFFFFFFFFFFFFFFFu;
     306             :     size_t offset;
     307             : 
     308             :     EXPECT(pool.add(&c, 8, offset) == kErrorOk);
     309             :     EXPECT(offset == kCount * 8 + 8);
     310             :   }
     311             : 
     312             :   INFO("Adding 2 byte constant to verify the gap is filled");
     313             :   {
     314             :     uint16_t c = 0xFFFE;
     315             :     size_t offset;
     316             : 
     317             :     EXPECT(pool.add(&c, 2, offset) == kErrorOk);
     318             :     EXPECT(offset == kCount * 8 + 2);
     319             :     EXPECT(pool.alignment() == 8);
     320             :   }
     321             : 
     322             :   INFO("Checking reset functionality");
     323             :   {
     324             :     pool.reset(&zone);
     325             :     zone.reset();
     326             : 
     327             :     EXPECT(pool.size() == 0);
     328             :     EXPECT(pool.alignment() == 0);
     329             :   }
     330             : 
     331             :   INFO("Checking pool alignment when combined constants are added");
     332             :   {
     333             :     uint8_t bytes[32] = { 0 };
     334             :     size_t offset;
     335             : 
     336             :     pool.add(bytes, 1, offset);
     337             :     EXPECT(pool.size() == 1);
     338             :     EXPECT(pool.alignment() == 1);
     339             :     EXPECT(offset == 0);
     340             : 
     341             :     pool.add(bytes, 2, offset);
     342             :     EXPECT(pool.size() == 4);
     343             :     EXPECT(pool.alignment() == 2);
     344             :     EXPECT(offset == 2);
     345             : 
     346             :     pool.add(bytes, 4, offset);
     347             :     EXPECT(pool.size() == 8);
     348             :     EXPECT(pool.alignment() == 4);
     349             :     EXPECT(offset == 4);
     350             : 
     351             :     pool.add(bytes, 4, offset);
     352             :     EXPECT(pool.size() == 8);
     353             :     EXPECT(pool.alignment() == 4);
     354             :     EXPECT(offset == 4);
     355             : 
     356             :     pool.add(bytes, 32, offset);
     357             :     EXPECT(pool.size() == 64);
     358             :     EXPECT(pool.alignment() == 32);
     359             :     EXPECT(offset == 32);
     360             :   }
     361             : }
     362             : #endif
     363             : 
     364             : ASMJIT_END_NAMESPACE
     365             : 
     366             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16