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