CIRCT 23.0.0git
Loading...
Searching...
No Matches
ESIRuntimeValuesTest.cpp
Go to the documentation of this file.
1//===- ESIRuntimeValuesTest.cpp - ESI Runtime Values System Tests ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "esi/Values.h"
10#include "gtest/gtest.h"
11#include <any>
12#include <cstdint>
13#include <limits>
14#include <map>
15#include <sstream>
16#include <stdexcept>
17#include <vector>
18
19using namespace esi;
20
21namespace {
22
23TEST(BitVectorTest, BasicConstructionAndAccess) {
24 std::vector<uint8_t> data{0xAA,
25 0x0F}; // 0b10101010 00001111 (LSB first per byte)
26 BitVector bv(data, 16); // non-owning view, width = 16
27 EXPECT_EQ(bv.width(), 16u);
28 // Check a few bits: byte layout little-endian bit ordering.
29 EXPECT_EQ(bv.getBit(0), false); // 0xAA LSB is 0
30 EXPECT_EQ(bv.getBit(1), true);
31 EXPECT_EQ(bv.getBit(2), false);
32 EXPECT_EQ(bv.getBit(3), true);
33 EXPECT_EQ(bv.getBit(7), true); // MSB of 0xAA
34 // Bits from second byte (0x0F): lower 4 bits 1111
35 EXPECT_TRUE(bv.getBit(8));
36 EXPECT_TRUE(bv.getBit(9));
37 EXPECT_TRUE(bv.getBit(10));
38 EXPECT_TRUE(bv.getBit(11));
39 EXPECT_FALSE(bv.getBit(12));
40}
41
42TEST(BitVectorTest, ShiftRightShrinksWidth) {
43 MutableBitVector bv(16);
44 // Set pattern 0b...0001111 (low 4 bits set)
45 for (int i = 0; i < 4; ++i)
46 bv.setBit(i, true);
47 bv >>= 2; // Drop two LSBs
48 EXPECT_EQ(bv.width(), 14u);
49 // New LSB should be original bit 2.
50 EXPECT_TRUE(bv.getBit(0));
51 EXPECT_TRUE(bv.getBit(1));
52 // Overshift beyond remaining width should throw (width currently 14).
53 EXPECT_THROW(bv >>= 20, std::out_of_range);
54}
55
56TEST(BitVectorTest, ShiftLeftIncreaseWidth) {
57 MutableBitVector bv(16);
58 for (int i = 0; i < 8; ++i)
59 bv.setBit(i, true); // lower byte = 0xFF
60 bv <<= 4; // Shift left by 4
61 EXPECT_EQ(bv.width(), 20u);
62
63 // New bits 0-3 should be 0.
64 for (int i = 0; i < 4; ++i)
65 EXPECT_FALSE(bv.getBit(i));
66
67 // Bits 4-11 should be original bits 0-7 (all 1).
68 for (int i = 4; i < 12; ++i)
69 EXPECT_TRUE(bv.getBit(i));
70
71 // Bits 12-19 should be 0.
72 for (int i = 12; i < 20; ++i)
73 EXPECT_FALSE(bv.getBit(i));
74}
75
76TEST(BitVectorTest, BitwiseOps) {
77 MutableBitVector a(8), b(8);
78 // a = 0b10101010, b = 0b11001100
79 for (int i = 0; i < 8; ++i) {
80 a.setBit(i, (i % 2) == 1); // set odd bits
81 b.setBit(i, (i / 2) % 2 ==
82 0); // pattern 0011 blocks -> reversed little-endian logic
83 }
84 auto c_and = a & b;
85 auto c_or = a | b;
86 auto c_xor = a ^ b;
87 EXPECT_EQ(c_and.width(), 8u);
88 // Validate a few bits
89 for (int i = 0; i < 8; ++i) {
90 bool abit = a.getBit(i);
91 bool bbit = b.getBit(i);
92 EXPECT_EQ(c_and.getBit(i), (abit & bbit));
93 EXPECT_EQ(c_or.getBit(i), (abit | bbit));
94 EXPECT_EQ(c_xor.getBit(i), (abit ^ bbit));
95 }
96}
97
98TEST(BitVectorTest, EqualityAlignedAndMisaligned) {
99 // Aligned equality with two views of the same underlying storage (aliasing)
100 // -- non-owning.
101 {
102 std::vector<uint8_t> raw{0xAA, 0x0F};
103 BitVector a0(raw, 16);
104 BitVector b0(raw, 16);
105 EXPECT_TRUE(a0 == b0);
106 }
107 // Now create two owning buffers with identical contents to ensure equality
108 // does not depend on pointer identity AND we can mutate one.
109 MutableBitVector a(std::vector<uint8_t>{0xAA, 0x0F}, 16);
110 MutableBitVector b(std::vector<uint8_t>{0xAA, 0x0F}, 16);
111 EXPECT_TRUE(a == b);
112 BitVector aShift = a >> 3; // width 13, misaligned view
113 BitVector bShift = b >> 3; // width 13
114 EXPECT_TRUE(aShift == bShift);
115 // Mutate only bShift's underlying storage (its owner).
116 // Note: slice returns an immutable view, but we can modify the original
117 // mutable source.
118 b.setBit(3, !b.getBit(3));
119 EXPECT_TRUE(aShift != bShift);
120}
121
122TEST(BitVectorTest, ZeroWidthAndBitwise) {
123 // Construct zero-width via explicit width 0.
124 MutableBitVector a(0); // width 0
125 MutableBitVector b(0);
126 EXPECT_EQ(a.width(), 0u);
127 EXPECT_EQ((a & b).width(), 0u);
128 EXPECT_EQ((a | b).width(), 0u);
129 EXPECT_EQ((a ^ b).width(), 0u);
130}
131
132TEST(BitVectorTest, ZeroWidthShiftBehavior) {
133 MutableBitVector z(0);
134 // Right shifting any positive amount should throw now.
135 EXPECT_THROW(z >>= 1, std::out_of_range);
136 // Right shifting by 0 is a no-op.
137 EXPECT_NO_THROW(z >>= 0);
138 // Left shifting zero-width is permitted.
139 EXPECT_NO_THROW(z <<= 5);
140 EXPECT_EQ(z.width(), 5u);
141}
142
143TEST(BitVectorTest, CrossByteAccessAndShift) {
144 std::vector<uint8_t> data{0xF0, 0x55, 0xCC};
145 MutableBitVector bv(std::move(data), 24);
146 bv >>= 3; // misalign
147 EXPECT_EQ(bv.width(), 21u);
148 // Original byte 0 (0xF0) bits LSB->MSB: 0 0 0 0 1 1 1 1. After dropping 3
149 // LSBs, new bit0 is old bit3 (0).
150 EXPECT_FALSE(bv.getBit(0));
151 // Spot check a cross-byte bit: choose bit 8 post-shift.
152 (void)bv.getBit(8); // Ensure no exceptions.
153}
154
155TEST(BitVectorTest, MisalignedBitwiseFallback) {
156 std::vector<uint8_t> da{0xAA, 0xCC};
157 std::vector<uint8_t> db{0x0F, 0xF0};
158 MutableBitVector a(std::move(da), 16);
159 MutableBitVector b(std::move(db), 16);
160 a >>= 3; // width 13, bitIndex=3
161 b >>= 3; // width 13, bitIndex=3
162 auto rAnd = a & b;
163 auto rOr = a | b;
164 auto rXor = a ^ b;
165 for (size_t i = 0; i < a.width(); ++i) {
166 bool abit = a.getBit(i);
167 bool bbit = b.getBit(i);
168 EXPECT_EQ(rAnd.getBit(i), (abit & bbit));
169 EXPECT_EQ(rOr.getBit(i), (abit | bbit));
170 EXPECT_EQ(rXor.getBit(i), (abit ^ bbit));
171 }
172}
173
174TEST(BitVectorTest, TailByteBitwiseNoMasking) {
175 // Width 13 -> final byte partially used; ensure operations stay in-bounds and
176 // correct for used bits.
177 std::vector<uint8_t> da{0b10101111, 0b00000001};
178 std::vector<uint8_t> db{0b11000011, 0b00000001};
179 MutableBitVector a(std::move(da), 13);
180 MutableBitVector b(std::move(db), 13);
181 auto r = a ^ b;
182 for (size_t i = 0; i < 13; ++i)
183 EXPECT_EQ(r.getBit(i), a.getBit(i) ^ b.getBit(i));
184}
185
186TEST(BitVectorTest, ConstructorInvalidBitIndex) {
187 std::vector<uint8_t> raw{0xAA, 0xBB};
188 EXPECT_THROW(BitVector(raw, 16, 8), std::invalid_argument); // bitIndex > 7
189}
190
191TEST(BitVectorTest, ConstructorWidthExceedsStorage) {
192 std::vector<uint8_t> raw{0xAA}; // 8 bits available
193 EXPECT_THROW(BitVector(raw, 16, 0), std::invalid_argument); // request 16
194}
195
196TEST(BitVectorTest, GetBitOutOfRange) {
197 std::vector<uint8_t> raw{0x44};
198 BitVector bv(raw);
199 EXPECT_THROW(bv.getBit(8), std::out_of_range);
200}
201
202TEST(BitVectorTest, SetBitOutOfRange) {
203 MutableBitVector bv(std::vector<uint8_t>{0x00}, 4);
204 EXPECT_THROW(bv.setBit(4, true), std::out_of_range);
205}
206
207TEST(BitVectorTest, ZeroWidthGetBitThrows) {
208 MutableBitVector z(std::vector<uint8_t>{0x00}, 0);
209 EXPECT_THROW(z.getBit(0), std::out_of_range);
210}
211
212TEST(BitVectorTest, BitwiseWidthMismatchThrows) {
213 std::vector<uint8_t> raw0{0x00};
214 std::vector<uint8_t> raw1{0x00};
215 BitVector a(raw0, 8);
216 BitVector b(raw1, 7);
217 EXPECT_THROW((void)(a & b), std::invalid_argument);
218 EXPECT_THROW((void)(a | b), std::invalid_argument);
219 EXPECT_THROW((void)(a ^ b), std::invalid_argument);
220}
221
222TEST(BitVectorTest, NonOwningModificationThrows) {
223 std::vector<uint8_t> raw{0x00, 0x00};
224 BitVector view(raw, 16); // non-owning
225 // BitVector is immutable, so setBit is not available on it
226 // This test verifies the immutable design: views cannot be modified
227 EXPECT_THROW(view.getBit(99), std::out_of_range); // Test bounds checking
228
229 // Owning instance (MutableBitVector) should allow modification.
230 MutableBitVector owned(std::vector<uint8_t>(2, 0), 16);
231 EXPECT_NO_THROW(owned.setBit(0, true));
232 EXPECT_TRUE(owned.getBit(0));
233
234 // Creating a view via implicit cast
235 BitVector trunc(owned); // Convert MutableBitVector to BitVector view
236 EXPECT_EQ(trunc.width(), 16u);
237 // The original owned can still be modified
238 owned.setBit(5, true);
239 EXPECT_TRUE(owned.getBit(5));
240}
241
242TEST(BitVectorTest, PackedSerializationRoundTrip) {
243 // Simplified test: pack values, create BitVector, extract values
244 // Pack: b1:1=1, u7:7=0x55, b2:1=0, s5:5=-5, b3:1=1, u9:9=0x1A5, u3:3=5
245 // => 27 bits total
246
247 uint64_t packed_value = 0;
248 size_t bit_offset = 0;
249
250 // Pack fields manually into packed_value
251 auto pack = [&](uint64_t value, size_t width) {
252 uint64_t mask = (width == 64) ? ~0ULL : ((1ULL << width) - 1ULL);
253 packed_value |= ((value & mask) << bit_offset);
254 bit_offset += width;
255 };
256
257 pack(1, 1); // b1 = true
258 pack(0x55, 7); // u7 = 0x55
259 pack(0, 1); // b2 = false
260 pack(27, 5); // s5 = -5 in 5-bit two's complement (0b11011 = 27)
261 pack(1, 1); // b3 = true
262 pack(0x1A5, 9); // u9 = 0x1A5
263 pack(5, 3); // u3 = 5
264
265 ASSERT_EQ(bit_offset, 27u);
266
267 // Create MutableBitVector from packed data
268 std::vector<uint8_t> bytes((27 + 7) / 8, 0);
269 for (size_t i = 0; i < bytes.size(); ++i) {
270 bytes[i] = static_cast<uint8_t>((packed_value >> (8 * i)) & 0xFF);
271 }
272
273 MutableBitVector stream(std::move(bytes), 27);
274
275 // Extract and verify
276 uint64_t rb1 = 0;
277 for (size_t i = 0; i < 1; ++i)
278 if (stream.getBit(i))
279 rb1 |= (1ULL << i);
280 EXPECT_EQ(rb1, 1u);
281
282 uint64_t ru7 = 0;
283 for (size_t i = 0; i < 7; ++i)
284 if (stream.getBit(1 + i))
285 ru7 |= (1ULL << i);
286 EXPECT_EQ(ru7, 0x55u);
287
288 uint64_t rb2 = 0;
289 for (size_t i = 0; i < 1; ++i)
290 if (stream.getBit(8 + i))
291 rb2 |= (1ULL << i);
292 EXPECT_EQ(rb2, 0u);
293
294 uint64_t rs5raw = 0;
295 for (size_t i = 0; i < 5; ++i)
296 if (stream.getBit(9 + i))
297 rs5raw |= (1ULL << i);
298 EXPECT_EQ(rs5raw, 27u); // -5 in 5-bit
299
300 uint64_t rb3 = 0;
301 for (size_t i = 0; i < 1; ++i)
302 if (stream.getBit(14 + i))
303 rb3 |= (1ULL << i);
304 EXPECT_EQ(rb3, 1u);
305
306 uint64_t ru9 = 0;
307 for (size_t i = 0; i < 9; ++i)
308 if (stream.getBit(15 + i))
309 ru9 |= (1ULL << i);
310 EXPECT_EQ(ru9, 0x1A5u);
311
312 uint64_t ru3 = 0;
313 for (size_t i = 0; i < 3; ++i)
314 if (stream.getBit(24 + i))
315 ru3 |= (1ULL << i);
316 EXPECT_EQ(ru3, 5u);
317}
318
319TEST(IntTest, ConstructionAndSignExtension) {
320 // Create a MutableBitVector with bit pattern for -5 in 8 bits (0xFB)
321 MutableBitVector mbv(8);
322 // -5 in 8-bit two's complement: 11111011 (LSB first: 1 1 0 1 1 1 1 1)
323 mbv.setBit(0, true);
324 mbv.setBit(1, true);
325 mbv.setBit(2, false);
326 mbv.setBit(3, true);
327 mbv.setBit(4, true);
328 mbv.setBit(5, true);
329 mbv.setBit(6, true);
330 mbv.setBit(7, true);
331
332 Int v(mbv); // Construct Int from BitVector view
333 EXPECT_EQ(static_cast<int64_t>(v), -5);
334 // Verify bit pattern
335 bool expected[8] = {true, true, false, true, true, true, true, true};
336 for (int i = 0; i < 8; ++i)
337 EXPECT_EQ(v.getBit(i), expected[i]) << "bit " << i;
338}
339
340TEST(IntTest, SignExtendOnNarrowTo64) {
341 // Create -1 in 12 bits (all bits set)
342 MutableBitVector mbv(12);
343 for (size_t i = 0; i < 12; ++i)
344 mbv.setBit(i, true);
345
346 Int v(mbv);
347 EXPECT_EQ(static_cast<int64_t>(v), -1);
348 // Check high (top) bit is 1.
349 EXPECT_TRUE(v.getBit(11));
350}
351
352TEST(IntTest, OverflowSigned) {
353 // Create a 70-bit value with bit 64 set (value that doesn't fit in signed 64)
354 MutableBitVector big(70);
355 big.setBit(64, true);
356 Int v(big);
357 EXPECT_THROW((void)static_cast<int64_t>(v), std::overflow_error);
358}
359
360TEST(IntTest, WidthOneValues) {
361 // Create 0 in 1 bit
362 MutableBitVector mbv_z(1);
363
364 // Create -1 in 1 bit (bit 0 set to 1)
365 MutableBitVector mbv_neg(1);
366 mbv_neg.setBit(0, true);
367
368 Int z(mbv_z);
369 Int neg(mbv_neg);
370
371 EXPECT_EQ(static_cast<int64_t>(z), 0);
372 EXPECT_EQ(static_cast<int64_t>(neg), -1);
373}
374
375TEST(IntTest, LargeWidthSignExtendedConversions) {
376 // Create -1 in 130 bits
377 MutableBitVector mbv_negAll(130);
378 for (size_t i = 0; i < 130; ++i)
379 mbv_negAll.setBit(i, true);
380
381 // Create 5 in 130 bits
382 MutableBitVector mbv_posSmall(130);
383 mbv_posSmall.setBit(0, true);
384 mbv_posSmall.setBit(2, true);
385
386 Int negAll(mbv_negAll);
387 Int posSmall(mbv_posSmall);
388
389 EXPECT_EQ(static_cast<int64_t>(negAll), -1);
390 EXPECT_EQ(static_cast<int64_t>(posSmall), 5);
391}
392
393TEST(IntTest, LargeWidthBadSignExtensionOverflow) {
394 // Create 5 in 130 bits and set bit 100
395 MutableBitVector mbv_v(130);
396 mbv_v.setBit(0, true);
397 mbv_v.setBit(2, true);
398 mbv_v.setBit(100, true);
399
400 Int v(mbv_v);
401 EXPECT_THROW((void)static_cast<int64_t>(v), std::overflow_error);
402}
403
404TEST(IntTest, LargeWidthBoundaryFitsInI64) {
405 // The wide value 2^63 (bit 63 set, all bits >= 64 clear) has a source
406 // top bit of 0 but cannot fit in int64_t -- its low 64 bits alias
407 // INT64_MIN. The narrowing must throw rather than silently flipping
408 // sign. Regression test for a bug where the high-byte equality check
409 // used the source's top bit instead of the destination's sign bit.
410 MutableBitVector mbv(128);
411 mbv.setBit(63, true);
412 Int v(mbv);
413 EXPECT_THROW((void)static_cast<int64_t>(v), std::overflow_error);
414
415 // The mirror case: bits 64..127 all set and bit 63 set is the wide
416 // representation of INT64_MIN; that one *does* fit.
417 MutableBitVector neg(128);
418 for (size_t i = 63; i < 128; ++i)
419 neg.setBit(i, true);
420 Int n(neg);
421 EXPECT_EQ(static_cast<int64_t>(n), std::numeric_limits<int64_t>::min());
422}
423
424TEST(UIntTest, BasicConstruction) {
425 // Create 123 in 16 bits
426 MutableBitVector mbv(16);
427 uint64_t val = 123;
428 for (size_t i = 0; i < 16; ++i)
429 if (val & (1ULL << i))
430 mbv.setBit(i, true);
431
432 UInt u(mbv);
433 EXPECT_EQ(static_cast<uint64_t>(u), 123u);
434 EXPECT_EQ(u.width(), 16u);
435}
436
437TEST(UIntTest, BitwiseFastVsFallbackConsistency) {
438 // Test that bitwise operations work correctly on MutableBitVectors
439 std::vector<uint8_t> raw1{0xFF, 0x0F, 0xAA};
440 std::vector<uint8_t> raw2{0x0F, 0xF0, 0x55};
441 MutableBitVector aAligned(std::move(raw1), 24);
442 MutableBitVector bAligned(std::move(raw2), 24);
443 auto alignedAnd = aAligned & bAligned; // aligned operation
444
445 // Verify result is correct
446 for (size_t i = 0; i < aAligned.width(); ++i)
447 EXPECT_EQ(alignedAnd.getBit(i), (aAligned.getBit(i) && bAligned.getBit(i)));
448}
449
450TEST(UIntTest, OverflowUnsignedConversion) {
451 // Create 70-bit zero
452 MutableBitVector big(70);
453 EXPECT_NO_THROW((void)static_cast<uint64_t>(UInt(big)));
454
455 // Set bit 65 to cause overflow
456 big.setBit(65, true);
457 UInt bigUInt(big);
458 EXPECT_THROW((void)static_cast<uint64_t>(bigUInt), std::overflow_error);
459}
460
461// New comprehensive formatting tests for BitVector string/stream output.
462TEST(BitVectorFormatTest, ToStringAndStreamBases) {
463 // Value: 0x1F3 = 499 dec = 0o763, choose width 12 to exercise leading zeros
464 // in binary.
465 const uint64_t val = 0x1F3ULL;
466 const size_t width =
467 12; // ensures three leading zero bits in binary representation
468 std::vector<uint8_t> bytes((width + 7) / 8, 0);
469 for (size_t i = 0; i < width; ++i)
470 if (val & (1ULL << i))
471 bytes[i / 8] |= static_cast<uint8_t>(1u << (i % 8));
472 // Create a MutableBitVector then get a view
473 MutableBitVector mbv(std::move(bytes), width);
474 BitVector bv = mbv; // implicit conversion to BitVector view
475
476 // Direct base conversions.
477 EXPECT_EQ(bv.toString(), std::string("1f3")); // default base=16
478 EXPECT_EQ(bv.toString(16), std::string("1f3"));
479 EXPECT_EQ(bv.toString(10), std::string("499"));
480 EXPECT_EQ(bv.toString(8), std::string("763"));
481 EXPECT_EQ(bv.toString(2), std::string("000111110011")); // width-length binary
482
483 // Stream formatting: dec (default), hex, oct, showbase, uppercase.
484 {
485 std::ostringstream oss;
486 oss << bv; // default dec
487 EXPECT_EQ(oss.str(), "499");
488 }
489 {
490 std::ostringstream oss;
491 oss << std::dec << std::showbase << bv; // showbase ignored for dec
492 EXPECT_EQ(oss.str(), "499");
493 }
494 {
495 std::ostringstream oss;
496 oss << std::hex << bv;
497 EXPECT_EQ(oss.str(), "1f3");
498 }
499 {
500 std::ostringstream oss;
501 oss << std::showbase << std::hex << bv;
502 EXPECT_EQ(oss.str(), "0x1f3");
503 }
504 {
505 std::ostringstream oss;
506 oss << std::uppercase << std::showbase << std::hex << bv;
507 EXPECT_EQ(oss.str(), "0X1F3");
508 }
509 {
510 std::ostringstream oss;
511 oss << std::oct << bv;
512 EXPECT_EQ(oss.str(), "763");
513 }
514 {
515 std::ostringstream oss;
516 oss << std::showbase << std::oct << bv;
517 EXPECT_EQ(oss.str(), "0763");
518 }
519
520 // Zero-width value formatting -> "0" in all bases.
521 {
522 MutableBitVector z_mut(0);
523 BitVector z(z_mut); // implicit conversion
524 std::ostringstream ossHex, ossDec, ossOct;
525 ossHex << std::hex << z;
526 ossDec << std::dec << z;
527 ossOct << std::oct << z;
528 EXPECT_EQ(ossHex.str(), "0");
529 EXPECT_EQ(ossDec.str(), "0");
530 EXPECT_EQ(ossOct.str(), "0");
531 EXPECT_EQ(z.toString(2), "0");
532 }
533}
534
535} // namespace
A lightweight, non-owning bit vector view backed by a byte array span.
Definition Values.h:51
std::string toString(unsigned base=16) const
Definition Values.cpp:362
A mutable bit vector that owns its underlying storage.
Definition Values.h:278
Definition esi.py:1