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
|