Skip to content

Commit 096b8a8

Browse files
[ADT] Fix an indexing bug in PackedVector (#158785)
PackedVector is like std::vector<int> except that we can store small elements (e.g. 2-bit elements) in a packed manner using a BitVector as the underlying storage. The problem is that for bit size 3 and beyond, the calculation of indices into the underlying BitVector is not correct. For example, around line 50, we see a "for" loop to retrieve an unsigned integer value: for (unsigned i = 0; i != BitNum-1; ++i) val = T(val | ((Bits[(Idx << (BitNum-1)) + i] ? 1UL : 0UL) << i)); Suppose that BitNum is 4 (that is, 4-bit item). Here is the mapping between the PackedVector index and the corresponding BitVector indices. Idx 0: 0, 1, 2, 3 Idx 1: 8, 9, 10, 11 Idx 2: 16, 17, 18, 19 That is, we use 4 bits out of every 8 bits. This is because the index calculation uses "<<". The index should really be Idx * BitNum + i. FWIW, all the methods in PackedVector consistently use the shift-based index calculation, so the user would never encounter a bug except possibly as excessive storage use. This patch fixes the index calculation. Now, in size(), I didn't want to do integer division: return Bits.size() / BitNum; so this patch adds a separate variable NumElements to keep track of the number of elements. The unit test checks for the expected size of the underlying BitVector.
1 parent b27bb09 commit 096b8a8

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

llvm/include/llvm/ADT/PackedVector.h

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ class PackedVectorBase<T, BitNum, BitVectorTy, false> {
3131
static T getValue(const BitVectorTy &Bits, unsigned Idx) {
3232
T val = T();
3333
for (unsigned i = 0; i != BitNum; ++i)
34-
val = T(val | ((Bits[(Idx << (BitNum-1)) + i] ? 1UL : 0UL) << i));
34+
val = T(val | ((Bits[(Idx * BitNum) + i] ? 1UL : 0UL) << i));
3535
return val;
3636
}
3737

3838
static void setValue(BitVectorTy &Bits, unsigned Idx, T val) {
3939
assert((val >> BitNum) == 0 && "value is too big");
4040
for (unsigned i = 0; i != BitNum; ++i)
41-
Bits[(Idx << (BitNum-1)) + i] = val & (T(1) << i);
41+
Bits[(Idx * BitNum) + i] = val & (T(1) << i);
4242
}
4343
};
4444

@@ -48,20 +48,20 @@ class PackedVectorBase<T, BitNum, BitVectorTy, true> {
4848
static T getValue(const BitVectorTy &Bits, unsigned Idx) {
4949
T val = T();
5050
for (unsigned i = 0; i != BitNum-1; ++i)
51-
val = T(val | ((Bits[(Idx << (BitNum-1)) + i] ? 1UL : 0UL) << i));
52-
if (Bits[(Idx << (BitNum-1)) + BitNum-1])
51+
val = T(val | ((Bits[(Idx * BitNum) + i] ? 1UL : 0UL) << i));
52+
if (Bits[(Idx * BitNum) + BitNum - 1])
5353
val = ~val;
5454
return val;
5555
}
5656

5757
static void setValue(BitVectorTy &Bits, unsigned Idx, T val) {
5858
if (val < 0) {
5959
val = ~val;
60-
Bits.set((Idx << (BitNum-1)) + BitNum-1);
60+
Bits.set((Idx * BitNum) + BitNum - 1);
6161
}
6262
assert((val >> (BitNum-1)) == 0 && "value is too big");
6363
for (unsigned i = 0; i != BitNum-1; ++i)
64-
Bits[(Idx << (BitNum-1)) + i] = val & (T(1) << i);
64+
Bits[(Idx * BitNum) + i] = val & (T(1) << i);
6565
}
6666
};
6767

@@ -76,6 +76,10 @@ template <typename T, unsigned BitNum, typename BitVectorTy = BitVector>
7676
class PackedVector : public PackedVectorBase<T, BitNum, BitVectorTy,
7777
std::numeric_limits<T>::is_signed> {
7878
BitVectorTy Bits;
79+
// Keep track of the number of elements on our own.
80+
// We always maintain Bits.size() == NumElements * BitNum.
81+
// Used to avoid an integer division in size().
82+
unsigned NumElements = 0;
7983
using base = PackedVectorBase<T, BitNum, BitVectorTy,
8084
std::numeric_limits<T>::is_signed>;
8185

@@ -99,17 +103,24 @@ class PackedVector : public PackedVectorBase<T, BitNum, BitVectorTy,
99103
};
100104

101105
PackedVector() = default;
102-
explicit PackedVector(unsigned size) : Bits(size << (BitNum-1)) {}
106+
explicit PackedVector(unsigned size)
107+
: Bits(size * BitNum), NumElements(size) {}
103108

104-
bool empty() const { return Bits.empty(); }
109+
bool empty() const { return NumElements == 0; }
105110

106-
unsigned size() const { return Bits.size() >> (BitNum - 1); }
111+
unsigned size() const { return NumElements; }
107112

108-
void clear() { Bits.clear(); }
113+
void clear() {
114+
Bits.clear();
115+
NumElements = 0;
116+
}
109117

110-
void resize(unsigned N) { Bits.resize(N << (BitNum - 1)); }
118+
void resize(unsigned N) {
119+
Bits.resize(N * BitNum);
120+
NumElements = N;
121+
}
111122

112-
void reserve(unsigned N) { Bits.reserve(N << (BitNum-1)); }
123+
void reserve(unsigned N) { Bits.reserve(N * BitNum); }
113124

114125
PackedVector &reset() {
115126
Bits.reset();

llvm/unittests/ADT/PackedVectorTest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ TEST(PackedVectorTest, Operation) {
6161
EXPECT_EQ(3U, Vec[1]);
6262
}
6363

64+
TEST(PackedVectorTest, RawBitsSize) {
65+
PackedVector<unsigned, 3> Vec;
66+
EXPECT_EQ(0u, Vec.raw_bits().size());
67+
Vec.push_back(2);
68+
Vec.push_back(0);
69+
Vec.push_back(1);
70+
Vec.push_back(3);
71+
EXPECT_EQ(12u, Vec.raw_bits().size());
72+
}
73+
6474
#ifdef EXPECT_DEBUG_DEATH
6575

6676
TEST(PackedVectorTest, UnsignedValues) {

0 commit comments

Comments
 (0)