CIRCT 22.0.0git
Loading...
Searching...
No Matches
Values.cpp
Go to the documentation of this file.
1//===- Values.cpp - ESI value system impl ----------------------*- C++ -*-===//
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 <algorithm>
11#include <cstring>
12#include <format>
13
14using namespace esi;
15
16BitVector::BitVector(std::span<const byte> bytes, std::optional<size_t> width,
17 uint8_t bitIndex)
18 : bitWidth(width ? *width : bytes.size() * 8), bitIndex(bitIndex) {
19 data = bytes;
20 if (bitIndex > 7)
21 throw std::invalid_argument("bitIndex must be <= 7");
22 size_t totalBitsAvail = bytes.size() * 8 - bitIndex;
23 if (bitWidth > totalBitsAvail)
24 throw std::invalid_argument(
25 std::format("Width of {} bits exceeds provided storage of {} bits",
26 bitWidth, totalBitsAvail));
27}
28
30 : data(other.data), bitWidth(other.bitWidth), bitIndex(other.bitIndex) {}
31
33 if (this == &other)
34 return *this;
35 data = other.data;
36 bitWidth = other.bitWidth;
37 bitIndex = other.bitIndex;
38 return *this;
39}
40
41bool BitVector::getBit(size_t i) const {
42 if (i >= bitWidth)
43 throw std::out_of_range("Bit index out of range");
44 size_t absoluteBit = bitIndex + i;
45 size_t byteIdx = absoluteBit / 8;
46 uint8_t bitOff = absoluteBit % 8;
47 return (data[byteIdx] >> bitOff) & 0x1;
48}
49
51 // Shifting by more than the current logical width is invalid.
52 if (n > bitWidth)
53 throw std::out_of_range("Right shift exceeds bit width");
54 // Shifting by exactly the width yields an empty (zero-width) vector.
55 if (n == bitWidth) {
57 return empty;
58 }
59
60 // Create a new view with adjusted bitIndex and width
61 BitVector result = *this;
62 result.bitWidth -= n;
63
64 // Compute new bitIndex and byte offset
65 size_t totalBitOffset = bitIndex + n;
66 size_t byteOffset = totalBitOffset / 8;
67 result.bitIndex = static_cast<uint8_t>(totalBitOffset % 8);
68
69 // Skip the appropriate number of bytes
70 if (byteOffset > 0 && byteOffset < data.size())
71 result.data = data.subspan(byteOffset);
72 else if (byteOffset >= data.size())
73 result.data = data.subspan(data.size());
74
75 return result;
76}
77
79 *this = *this >> n;
80 return *this;
81}
82
83BitVector BitVector::slice(size_t offset, size_t sliceWidth) const {
84 if (offset > bitWidth || sliceWidth > bitWidth - offset)
85 throw std::invalid_argument("slice range exceeds current width");
86 // Return a new view with adjusted bitIndex
87 BitVector result;
88 result.bitWidth = sliceWidth;
89 result.bitIndex = static_cast<uint8_t>(bitIndex + (offset % 8));
90 size_t byteOffset = offset / 8;
91 result.data = data.subspan(byteOffset);
92 if (result.bitIndex >= 8) {
93 result.bitIndex -= 8;
94 result.data = result.data.subspan(1);
95 }
96 return result;
97}
98
99MutableBitVector::MutableBitVector(size_t width) : owner((width + 7) / 8, 0) {
100 bitWidth = width;
101 bitIndex = 0;
102 data = std::span<const byte>(owner.data(), owner.size());
103}
104
105MutableBitVector::MutableBitVector(std::vector<byte> &&bytes,
106 std::optional<size_t> width)
107 : owner(std::move(bytes)) {
108 bitWidth = width ? *width : owner.size() * 8;
109 bitIndex = 0;
110 data = std::span<const byte>(owner.data(), owner.size());
111 if (bitWidth > data.size() * 8)
112 throw std::invalid_argument("Width exceeds provided storage");
113}
114
116 : BitVector(), owner(other.owner) {
117 bitWidth = other.bitWidth;
118 bitIndex = 0;
119 data = std::span<const byte>(owner.data(), owner.size());
120}
121
123 : BitVector(), owner(std::vector<byte>((other.width() + 7) / 8, 0)) {
124 bitWidth = other.width();
125 bitIndex = 0;
126 data = std::span<const byte>(owner.data(), owner.size());
127 // Copy bits from source
128 for (size_t i = 0; i < bitWidth; ++i)
129 if (other.getBit(i))
130 setBit(i, true);
131}
132
134 : BitVector(), owner(std::vector<byte>((other.width() + 7) / 8, 0)) {
135 bitWidth = other.width();
136 bitIndex = 0;
137 data = std::span<const byte>(owner.data(), owner.size());
138 // Copy bits from source
139 for (size_t i = 0; i < bitWidth; ++i)
140 if (other.getBit(i))
141 setBit(i, true);
142}
143
145 : BitVector(), owner(std::move(other.owner)) {
146 bitWidth = other.bitWidth;
147 bitIndex = 0;
148 data = std::span<const byte>(owner.data(), owner.size());
149 other.data = {};
150 other.bitWidth = 0;
151 other.bitIndex = 0;
152}
153
155 if (this == &other)
156 return *this;
157 owner = other.owner;
158 bitWidth = other.bitWidth;
159 bitIndex = 0;
160 data = std::span<const byte>(owner.data(), owner.size());
161 return *this;
162}
163
166 if (this == &other)
167 return *this;
168 owner = std::move(other.owner);
169 bitWidth = other.bitWidth;
170 bitIndex = 0;
171 data = std::span<const byte>(owner.data(), owner.size());
172 other.data = {};
173 other.bitWidth = 0;
174 other.bitIndex = 0;
175 return *this;
176}
177
178void MutableBitVector::setBit(size_t i, bool v) {
179 if (i >= bitWidth)
180 throw std::out_of_range("Bit index out of range");
181 size_t byteIdx = i / 8;
182 uint8_t bitOff = i % 8;
183 uint8_t mask = static_cast<uint8_t>(1u << bitOff);
184 byte &target = owner[byteIdx];
185 if (v)
186 target |= mask;
187 else
188 target &= static_cast<uint8_t>(~mask);
189}
190
192 // Shifting by more than the current logical width is invalid.
193 if (n > bitWidth)
194 throw std::out_of_range("Right shift exceeds bit width");
195 // Shifting by exactly the width yields an empty (zero-width) vector.
196 if (n == bitWidth) {
198 return *this;
199 }
201 bitIndex = static_cast<uint8_t>(bitIndex + n);
202 while (bitIndex >= 8) {
204 if (data.size() == 0)
205 break;
206 data = data.subspan(1);
207 }
208 return *this;
209}
210
212 // Extend width by n bits, shifting existing bits upward by n and inserting
213 // n zero bits at the least-significant positions.
214 if (n == 0)
215 return *this;
216 size_t oldWidth = bitWidth;
217 size_t newWidth = oldWidth + n;
218 // Always produce an owning, tightly packed representation with bitIndex=0.
219 std::vector<byte> newStorage((newWidth + 7) / 8, 0);
220 // Copy old bits to new positions.
221 for (size_t i = 0; i < oldWidth; ++i)
222 if (getBit(i)) {
223 size_t newPos = i + n; // shifted upward
224 newStorage[newPos / 8] |= static_cast<byte>(1u << (newPos % 8));
225 }
226 owner = std::move(newStorage);
227 data = std::span<const byte>(owner.data(), owner.size());
228 bitWidth = newWidth;
229 bitIndex = 0;
230 return *this;
231}
232
234 // Concatenation: append bits from other (must create new owning storage)
235 size_t oldWidth = bitWidth;
236 size_t otherWidth = other.width();
237 size_t newWidth = oldWidth + otherWidth;
238 std::vector<byte> newStorage((newWidth + 7) / 8, 0);
239 // Copy this's bits
240 for (size_t i = 0; i < oldWidth; ++i)
241 if (getBit(i)) {
242 newStorage[i / 8] |= static_cast<byte>(1u << (i % 8));
243 }
244 // Copy other's bits offset by oldWidth
245 for (size_t i = 0; i < otherWidth; ++i)
246 if (other.getBit(i)) {
247 size_t newPos = i + oldWidth;
248 newStorage[newPos / 8] |= static_cast<byte>(1u << (newPos % 8));
249 }
250 owner = std::move(newStorage);
251 data = std::span<const byte>(owner.data(), owner.size());
252 bitWidth = newWidth;
253 bitIndex = 0;
254 return *this;
255}
256
257MutableBitVector esi::operator&(const BitVector &a, const BitVector &b) {
258 if (a.width() != b.width())
259 throw std::invalid_argument("Bitwise & require equal widths");
260 MutableBitVector result(a.width());
261 for (size_t i = 0; i < a.width(); ++i)
262 if (a.getBit(i) && b.getBit(i))
263 result.setBit(i, true);
264 return result;
265}
266
267MutableBitVector esi::operator|(const BitVector &a, const BitVector &b) {
268 if (a.width() != b.width())
269 throw std::invalid_argument("Bitwise ops require equal widths");
270 MutableBitVector result(a.width());
271 for (size_t i = 0; i < a.width(); ++i)
272 if (a.getBit(i) || b.getBit(i))
273 result.setBit(i, true);
274 return result;
275}
276
277MutableBitVector esi::operator^(const BitVector &a, const BitVector &b) {
278 if (a.width() != b.width())
279 throw std::invalid_argument("Bitwise ops require equal widths");
280 MutableBitVector result(a.width());
281 for (size_t i = 0; i < a.width(); ++i)
282 if (a.getBit(i) != b.getBit(i))
283 result.setBit(i, true);
284 return result;
285}
286
287// Helper: convert to hexadecimal / octal (power-of-two bases) without
288// allocating large temporaries. bitsPerDigit must be 1/3/4.
289static std::string toPowerOfTwoString(const BitVector &bv,
290 unsigned bitsPerDigit, bool uppercase) {
291 if (bv.width() == 0)
292 return "0";
293 unsigned groups = (bv.width() + bitsPerDigit - 1) / bitsPerDigit;
294 std::string out;
295 out.reserve(groups);
296 auto hexDigit = [&](unsigned v) -> char {
297 if (v < 10)
298 return static_cast<char>('0' + v);
299 return static_cast<char>((uppercase ? 'A' : 'a') + (v - 10));
300 };
301 for (int g = static_cast<int>(groups) - 1; g >= 0; --g) {
302 unsigned value = 0;
303 for (unsigned j = 0; j < bitsPerDigit; ++j) {
304 unsigned bitIdx = g * bitsPerDigit + j;
305 if (bitIdx < bv.width() && bv.getBit(bitIdx))
306 value |= (1u << j); // LSB-first inside digit
307 }
308 if (out.empty() && value == 0 && g != 0)
309 continue; // skip leading zeros
310 if (bitsPerDigit == 4)
311 out.push_back(hexDigit(value));
312 else // octal (3 bits) or binary (1 bit)
313 out.push_back(static_cast<char>('0' + value));
314 }
315 if (out.empty())
316 return "0"; // all zeros
317 return out;
318}
319
320// Helper: decimal conversion via base 1e9 limbs (little-endian).
321static std::string toDecimalString(const BitVector &bv) {
322 if (bv.width() == 0)
323 return "0";
324 constexpr uint32_t BASE = 1000000000u; // 1e9
325 std::vector<uint32_t> limbs(1, 0);
326 // Iterate MSB->LSB so we can perform: acc = acc*2 + bit
327 for (size_t idx = 0; idx < bv.width(); ++idx) {
328 size_t i = bv.width() - 1 - idx;
329 // acc *= 2
330 uint64_t carry = 0;
331 for (auto &d : limbs) {
332 uint64_t v = static_cast<uint64_t>(d) * 2 + carry;
333 d = static_cast<uint32_t>(v % BASE);
334 carry = v / BASE;
335 }
336 if (carry)
337 limbs.push_back(static_cast<uint32_t>(carry));
338 // acc += bit
339 if (bv.getBit(i)) {
340 uint64_t c = 1;
341 for (auto &d : limbs) {
342 uint64_t v = static_cast<uint64_t>(d) + c;
343 d = static_cast<uint32_t>(v % BASE);
344 c = v / BASE;
345 if (!c)
346 break;
347 }
348 if (c)
349 limbs.push_back(static_cast<uint32_t>(c));
350 }
351 }
352 // Convert limbs to string.
353 std::string out = std::to_string(limbs.back());
354 for (int i = static_cast<int>(limbs.size()) - 2; i >= 0; --i) {
355 std::string chunk = std::to_string(limbs[i]);
356 out.append(9 - chunk.size(), '0');
357 out += chunk;
358 }
359 return out;
360}
361
362std::string BitVector::toString(unsigned base) const {
363 switch (base) {
364 case 2: {
365 // Binary (MSB -> LSB) representation.
366 if (bitWidth == 0)
367 return std::string("0");
368 std::string s;
369 s.reserve(bitWidth);
370 for (size_t i = 0; i < bitWidth; ++i)
371 s.push_back(getBit(bitWidth - 1 - i) ? '1' : '0');
372 return s;
373 }
374 case 8:
375 return toPowerOfTwoString(*this, 3, false);
376 case 16:
377 return toPowerOfTwoString(*this, 4, false);
378 case 10:
379 return toDecimalString(*this);
380 default:
381 throw std::invalid_argument(
382 std::format("Unsupported base '{}' for BitVector::toString", base));
383 }
384}
385
386std::ostream &esi::operator<<(std::ostream &os, const BitVector &bv) {
387 using std::ios_base;
388 ios_base::fmtflags basefmt = os.flags() & ios_base::basefield;
389 bool showBase = (os.flags() & ios_base::showbase) != 0;
390 bool upper = (os.flags() & ios_base::uppercase) != 0;
391 std::string body;
392 switch (basefmt) {
393 case ios_base::hex: {
394 body = toPowerOfTwoString(bv, 4, upper);
395 if (showBase)
396 os << (upper ? "0X" : "0x");
397 os << body;
398 return os;
399 }
400 case ios_base::oct: {
401 body = toPowerOfTwoString(bv, 3, false);
402 if (showBase)
403 os << '0';
404 os << body;
405 return os;
406 }
407 case ios_base::dec: {
408 body = toDecimalString(bv);
409 os << body; // showbase ignored for dec (matches standard)
410 return os;
411 }
412 default: { // Fallback: binary string (no standard manipulator for this)
413 body = bv.toString(2);
414 os << body;
415 return os;
416 }
417 }
418}
419
420bool BitVector::operator==(const BitVector &rhs) const {
421 if (bitWidth != rhs.bitWidth)
422 return false;
423 return std::ranges::equal(*this, rhs);
424}
425
426UInt::UInt(uint64_t v, unsigned width) : MutableBitVector(width) {
427 if (width > 0 && width < 64 && (v >> width) != 0)
428 throw std::overflow_error(
429 std::format("Value {} does not fit in {} bits", v, width));
430 for (size_t i = 0; i < width; ++i)
431 if ((v >> i) & 1)
432 setBit(i, true);
433}
434
435Int::Int(int64_t v, unsigned width) : MutableBitVector(width) {
436 if (width > 0 && width < 64) {
437 int64_t maxVal = (1LL << (width - 1)) - 1;
438 int64_t minVal = -(1LL << (width - 1));
439 if (v < minVal || v > maxVal)
440 throw std::overflow_error(
441 std::format("Value {} does not fit in {} bits", v, width));
442 }
443 for (size_t i = 0; i < width; ++i)
444 if ((v >> i) & 1)
445 setBit(i, true);
446}
447
449 if (bitWidth != other.bitWidth)
450 throw std::invalid_argument("Bitwise &= requires equal widths");
451 for (size_t i = 0; i < bitWidth; ++i)
452 setBit(i, getBit(i) && other.getBit(i));
453 return *this;
454}
455
457 if (bitWidth < other.bitWidth)
458 throw std::invalid_argument("Bitwise |= requires <= widths");
459 for (size_t i = 0; i < other.bitWidth; ++i)
460 setBit(i, getBit(i) || other.getBit(i));
461 return *this;
462}
463
465 if (bitWidth != other.bitWidth)
466 throw std::invalid_argument("Bitwise ^= requires equal widths");
467 for (size_t i = 0; i < bitWidth; ++i)
468 setBit(i, getBit(i) != other.getBit(i));
469 return *this;
470}
473 for (size_t i = 0; i < bitWidth; ++i)
474 res.setBit(i, !getBit(i));
475 return res;
476}
479 MutableBitVector result(static_cast<const BitVector &>(*this));
480 result |= other;
481 return result;
482}
483
486 MutableBitVector result(static_cast<const BitVector &>(*this));
487 result &= other;
488 return result;
489}
490
493 MutableBitVector result(static_cast<const BitVector &>(*this));
494 result ^= other;
495 return result;
496}
497
498int64_t Int::toI64() const {
499 if (this->bitWidth == 0)
500 return 0;
501 uint64_t u = 0;
502 size_t limit = this->bitWidth < 64 ? this->bitWidth : 64;
503 for (size_t i = 0; i < limit; ++i)
504 if (this->getBit(i))
505 u |= (1ULL << i);
506 bool signBit = this->getBit(this->bitWidth - 1);
507 if (this->bitWidth < 64) {
508 if (signBit) {
509 for (size_t i = this->bitWidth; i < 64; ++i)
510 u |= (1ULL << i);
511 }
512 return static_cast<int64_t>(u);
513 }
514 for (size_t i = 64; i < this->bitWidth; ++i) {
515 if (this->getBit(i) != signBit)
516 throw std::overflow_error("Int does not fit in int64_t");
517 }
518 return static_cast<int64_t>(u);
519}
520
521void Int::fits(int64_t v, unsigned n) {
522 if (n == 0 || n > 64)
523 throw std::invalid_argument(std::format("Invalid bit width: {}", n));
524 int64_t min = -(1LL << (n - 1));
525 int64_t max = (1LL << (n - 1)) - 1;
526 if (v < min || v > max)
527 throw std::overflow_error(
528 std::format("Value {} does not fit in int{}_t", v, n));
529}
530
531uint64_t UInt::toUI64() const {
532 if (this->bitWidth > 64) {
533 for (size_t i = 64; i < this->bitWidth; ++i)
534 if (this->getBit(i))
535 throw std::overflow_error("Int does not fit in uint64_t");
536 }
537 uint64_t u = 0;
538 size_t limit = this->bitWidth < 64 ? this->bitWidth : 64;
539 for (size_t i = 0; i < limit; ++i)
540 if (this->getBit(i))
541 u |= (1ULL << i);
542 return u;
543}
544
545void UInt::fits(uint64_t v, unsigned n) {
546 if (n == 0 || n > 64)
547 throw std::invalid_argument(std::format("Invalid bit width: {}", n));
548 uint64_t max = (n == 64) ? UINT64_MAX : ((1ULL << n) - 1);
549 if (v > max)
550 throw std::overflow_error(
551 std::format("Value {} does not fit in uint{}_t", v, n));
552}
static InstancePath empty
static std::string toPowerOfTwoString(const BitVector &bv, unsigned bitsPerDigit, bool uppercase)
Definition Values.cpp:289
static std::string toDecimalString(const BitVector &bv)
Definition Values.cpp:321
A lightweight, non-owning bit vector view backed by a byte array span.
Definition Values.h:42
size_t width() const
Definition Values.h:54
friend MutableBitVector operator^(const BitVector &a, const BitVector &b)
Bitwise XOR: creates a new MutableBitVector with the result.
size_t bitWidth
Definition Values.h:179
friend MutableBitVector operator&(const BitVector &a, const BitVector &b)
Bitwise AND: creates a new MutableBitVector with the result.
BitVector()=default
uint8_t byte
Definition Values.h:44
BitVector slice(size_t offset, size_t sliceWidth) const
Create a new immutable view of a contiguous bit slice [offset, offset+sliceWidth).
Definition Values.cpp:83
BitVector & operator=(const BitVector &other)
Definition Values.cpp:32
bool getBit(size_t i) const
Return the i-th bit (0 = LSB) as boolean.
Definition Values.cpp:41
std::string toString(unsigned base=16) const
Definition Values.cpp:362
std::span< const byte > data
Definition Values.h:178
BitVector & operator>>=(size_t n)
Definition Values.cpp:78
BitVector operator>>(size_t n) const
Logical right shift that drops the least-significant n bits by advancing the byte/bit index and reduc...
Definition Values.cpp:50
uint8_t bitIndex
Definition Values.h:180
bool operator==(const BitVector &rhs) const
Definition Values.cpp:420
friend MutableBitVector operator|(const BitVector &a, const BitVector &b)
Bitwise OR: creates a new MutableBitVector with the result.
int64_t toI64() const
Definition Values.cpp:498
Int()=default
static void fits(int64_t v, unsigned n)
Definition Values.cpp:521
A mutable bit vector that owns its underlying storage.
Definition Values.h:185
MutableBitVector & operator|=(const MutableBitVector &other)
Definition Values.cpp:456
MutableBitVector & operator>>=(size_t n)
In-place logical right shift that drops the least-significant n bits.
Definition Values.cpp:191
MutableBitVector & operator<<=(size_t n)
In-place logical left shift shifts in n zero bits at LSB, shifting existing bits upward.
Definition Values.cpp:211
MutableBitVector operator~() const
Definition Values.cpp:471
MutableBitVector & operator&=(const MutableBitVector &other)
Definition Values.cpp:448
MutableBitVector()=default
std::vector< byte > owner
Definition Values.h:243
void setBit(size_t i, bool v)
Set the i-th bit.
Definition Values.cpp:178
MutableBitVector & operator=(const MutableBitVector &other)
Definition Values.cpp:154
MutableBitVector & operator^=(const MutableBitVector &other)
Definition Values.cpp:464
static void fits(uint64_t v, unsigned n)
Definition Values.cpp:545
UInt()=default
uint64_t toUI64() const
Definition Values.cpp:531
Definition esi.py:1
std::ostream & operator<<(std::ostream &os, const BitVector &bv)
Definition Values.cpp:386