8296139: Make GrowableBitMap the base class of all implementations

Reviewed-by: aboldtch, stefank, simonis
This commit is contained in:
Xin Liu 2022-11-09 19:51:54 +00:00
parent cc8bf95046
commit 1b94ae13d3
3 changed files with 138 additions and 196 deletions

View File

@ -31,56 +31,21 @@
#include "utilities/debug.hpp"
#include "utilities/population_count.hpp"
STATIC_ASSERT(sizeof(BitMap::bm_word_t) == BytesPerWord); // "Implementation assumption."
using bm_word_t = BitMap::bm_word_t;
using idx_t = BitMap::idx_t;
typedef BitMap::bm_word_t bm_word_t;
typedef BitMap::idx_t idx_t;
STATIC_ASSERT(sizeof(bm_word_t) == BytesPerWord); // "Implementation assumption."
class ResourceBitMapAllocator : StackObj {
public:
bm_word_t* allocate(idx_t size_in_words) const {
return NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);
}
void free(bm_word_t* map, idx_t size_in_words) const {
// Don't free resource allocated arrays.
}
};
class CHeapBitMapAllocator : StackObj {
MEMFLAGS _flags;
public:
CHeapBitMapAllocator(MEMFLAGS flags) : _flags(flags) {}
bm_word_t* allocate(size_t size_in_words) const {
return ArrayAllocator<bm_word_t>::allocate(size_in_words, _flags);
}
void free(bm_word_t* map, idx_t size_in_words) const {
ArrayAllocator<bm_word_t>::free(map, size_in_words);
}
};
class ArenaBitMapAllocator : StackObj {
Arena* _arena;
public:
ArenaBitMapAllocator(Arena* arena) : _arena(arena) {}
bm_word_t* allocate(idx_t size_in_words) const {
return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);
}
void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
};
template <class Allocator>
BitMap::bm_word_t* BitMap::reallocate(const Allocator& allocator, bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {
template <class BitMapWithAllocator>
bm_word_t* GrowableBitMap<BitMapWithAllocator>::reallocate(bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {
size_t old_size_in_words = calc_size_in_words(old_size_in_bits);
size_t new_size_in_words = calc_size_in_words(new_size_in_bits);
bm_word_t* map = NULL;
BitMapWithAllocator* derived = static_cast<BitMapWithAllocator*>(this);
if (new_size_in_words > 0) {
map = allocator.allocate(new_size_in_words);
map = derived->allocate(new_size_in_words);
if (old_map != NULL) {
Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map,
@ -99,85 +64,45 @@ BitMap::bm_word_t* BitMap::reallocate(const Allocator& allocator, bm_word_t* old
}
if (old_map != NULL) {
allocator.free(old_map, old_size_in_words);
derived->free(old_map, old_size_in_words);
}
return map;
}
template <class Allocator>
bm_word_t* BitMap::allocate(const Allocator& allocator, idx_t size_in_bits, bool clear) {
// Reuse reallocate to ensure that the new memory is cleared.
return reallocate(allocator, NULL, 0, size_in_bits, clear);
ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear)
: GrowableBitMap<ArenaBitMap>(), _arena(arena) {
initialize(size_in_bits, clear);
}
template <class Allocator>
void BitMap::free(const Allocator& allocator, bm_word_t* map, idx_t size_in_bits) {
bm_word_t* ret = reallocate(allocator, map, size_in_bits, 0);
assert(ret == NULL, "Reallocate shouldn't have allocated");
}
template <class Allocator>
void BitMap::resize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {
bm_word_t* new_map = reallocate(allocator, map(), size(), new_size_in_bits, clear);
update(new_map, new_size_in_bits);
}
template <class Allocator>
void BitMap::initialize(const Allocator& allocator, idx_t size_in_bits, bool clear) {
assert(map() == NULL, "precondition");
assert(size() == 0, "precondition");
resize(allocator, size_in_bits, clear);
}
template <class Allocator>
void BitMap::reinitialize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {
// Remove previous bits - no need to clear
resize(allocator, 0, false /* clear */);
initialize(allocator, new_size_in_bits, clear);
bm_word_t* ArenaBitMap::allocate(idx_t size_in_words) const {
return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);
}
ResourceBitMap::ResourceBitMap(idx_t size_in_bits, bool clear)
: BitMap(allocate(ResourceBitMapAllocator(), size_in_bits, clear), size_in_bits) {
: GrowableBitMap<ResourceBitMap>() {
initialize(size_in_bits, clear);
}
void ResourceBitMap::resize(idx_t new_size_in_bits) {
BitMap::resize(ResourceBitMapAllocator(), new_size_in_bits, true /* clear */);
}
void ResourceBitMap::initialize(idx_t size_in_bits) {
BitMap::initialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);
}
void ResourceBitMap::reinitialize(idx_t size_in_bits) {
BitMap::reinitialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);
}
ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits)
: BitMap(allocate(ArenaBitMapAllocator(arena), size_in_bits), size_in_bits) {
bm_word_t* ResourceBitMap::allocate(idx_t size_in_words) const {
return (bm_word_t*)NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);
}
CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear)
: BitMap(allocate(CHeapBitMapAllocator(flags), size_in_bits, clear), size_in_bits), _flags(flags) {
: GrowableBitMap<CHeapBitMap>(), _flags(flags) {
initialize(size_in_bits, clear);
}
CHeapBitMap::~CHeapBitMap() {
free(CHeapBitMapAllocator(_flags), map(), size());
free(map(), size());
}
void CHeapBitMap::resize(idx_t new_size_in_bits, bool clear) {
BitMap::resize(CHeapBitMapAllocator(_flags), new_size_in_bits, clear);
bm_word_t* CHeapBitMap::allocate(idx_t size_in_words) const {
return ArrayAllocator<bm_word_t>::allocate(size_in_words, _flags);
}
void CHeapBitMap::initialize(idx_t size_in_bits, bool clear) {
BitMap::initialize(CHeapBitMapAllocator(_flags), size_in_bits, clear);
}
void CHeapBitMap::reinitialize(idx_t size_in_bits, bool clear) {
BitMap::reinitialize(CHeapBitMapAllocator(_flags), size_in_bits, clear);
void CHeapBitMap::free(bm_word_t* map, idx_t size_in_words) const {
ArrayAllocator<bm_word_t>::free(map, size_in_words);
}
#ifdef ASSERT
@ -706,3 +631,7 @@ void BitMap::print_on(outputStream* st) const {
}
#endif
template class GrowableBitMap<ArenaBitMap>;
template class GrowableBitMap<ResourceBitMap>;
template class GrowableBitMap<CHeapBitMap>;

View File

@ -64,6 +64,7 @@ class BitMap {
bm_word_t* _map; // First word in bitmap
idx_t _size; // Size of bitmap (in bits)
protected:
// The maximum allowable size of a bitmap, in words or bits.
// Limit max_size_in_bits so aligning up to a word boundary never overflows.
static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); }
@ -110,7 +111,6 @@ class BitMap {
static bool is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word);
protected:
// Return the position of bit within the word that contains it (e.g., if
// bitmap words are 32 bits, return a number 0 <= n <= 31).
static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); }
@ -161,42 +161,6 @@ class BitMap {
idx_t count_one_bits_within_word(idx_t beg, idx_t end) const;
idx_t count_one_bits_in_range_of_words(idx_t beg_full_word, idx_t end_full_word) const;
// Allocation Helpers.
// Allocates and clears the bitmap memory.
template <class Allocator>
static bm_word_t* allocate(const Allocator&, idx_t size_in_bits, bool clear = true);
// Reallocates and clears the new bitmap memory.
template <class Allocator>
static bm_word_t* reallocate(const Allocator&, bm_word_t* map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear = true);
// Free the bitmap memory.
template <class Allocator>
static void free(const Allocator&, bm_word_t* map, idx_t size_in_bits);
// Protected functions, that are used by BitMap sub-classes that support them.
// Resize the backing bitmap memory.
//
// Old bits are transferred to the new memory
// and the extended memory is cleared.
template <class Allocator>
void resize(const Allocator& allocator, idx_t new_size_in_bits, bool clear);
// Set up and clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or (re)initialize.
template <class Allocator>
void initialize(const Allocator& allocator, idx_t size_in_bits, bool clear);
// Set up and clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
template <class Allocator>
void reinitialize(const Allocator& allocator, idx_t new_size_in_bits, bool clear);
// Set the map and size.
void update(bm_word_t* map, idx_t size) {
_map = map;
@ -354,84 +318,108 @@ class BitMap {
#endif
};
// A concrete implementation of the "abstract" BitMap class.
// CRTP: BitmapWithAllocator exposes the following Allocator interfaces upward to GrowableBitMap.
//
// The BitMapView is used when the backing storage is managed externally.
class BitMapView : public BitMap {
public:
BitMapView() : BitMap(NULL, 0) {}
BitMapView(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
};
// bm_word_t* allocate(idx_t size_in_words) const;
// void free(bm_word_t* map, idx_t size_in_words) const
//
template <class BitMapWithAllocator>
class GrowableBitMap : public BitMap {
protected:
bm_word_t* reallocate(bm_word_t* map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear);
// A BitMap with storage in a ResourceArea.
class ResourceBitMap : public BitMap {
GrowableBitMap() : GrowableBitMap(nullptr, 0) {}
GrowableBitMap(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
public:
ResourceBitMap() : BitMap(NULL, 0) {}
// Conditionally clears the bitmap memory.
ResourceBitMap(idx_t size_in_bits, bool clear = true);
// Set up and clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or (re)initialize.
void initialize(idx_t size_in_bits, bool clear = true) {
assert(map() == NULL, "precondition");
assert(size() == 0, "precondition");
resize(size_in_bits, clear);
}
// Set up and clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t new_size_in_bits, bool clear = true) {
// Remove previous bits - no need to clear
resize(0, false /* clear */);
initialize(new_size_in_bits, clear);
}
// Protected functions, that are used by BitMap sub-classes that support them.
// Resize the backing bitmap memory.
//
// Old bits are transferred to the new memory
// and the extended memory is cleared.
void resize(idx_t new_size_in_bits);
void resize(idx_t new_size_in_bits, bool clear = true) {
bm_word_t* new_map = reallocate(map(), size(), new_size_in_bits, clear);
update(new_map, new_size_in_bits);
}
};
// Set up and clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or initialize.
void initialize(idx_t size_in_bits);
// Set up and clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t size_in_bits);
// A concrete implementation of the "abstract" BitMap class.
//
// The BitMapView is used when the backing storage is managed externally.
class BitMapView : public BitMap {
public:
BitMapView() : BitMapView(nullptr, 0) {}
BitMapView(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
};
// A BitMap with storage in a specific Arena.
class ArenaBitMap : public BitMap {
class ArenaBitMap : public GrowableBitMap<ArenaBitMap> {
Arena* const _arena;
NONCOPYABLE(ArenaBitMap);
public:
// Clears the bitmap memory.
ArenaBitMap(Arena* arena, idx_t size_in_bits);
ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear = true);
private:
NONCOPYABLE(ArenaBitMap);
bm_word_t* allocate(idx_t size_in_words) const;
void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
};
// A BitMap with storage in the current threads resource area.
class ResourceBitMap : public GrowableBitMap<ResourceBitMap> {
public:
ResourceBitMap() : ResourceBitMap(0) {}
ResourceBitMap(idx_t size_in_bits, bool clear = true);
bm_word_t* allocate(idx_t size_in_words) const;
void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
};
// A BitMap with storage in the CHeap.
class CHeapBitMap : public BitMap {
class CHeapBitMap : public GrowableBitMap<CHeapBitMap> {
// NMT memory type
const MEMFLAGS _flags;
private:
// Don't allow copy or assignment, to prevent the
// allocated memory from leaking out to other instances.
NONCOPYABLE(CHeapBitMap);
// NMT memory type
MEMFLAGS _flags;
public:
CHeapBitMap(MEMFLAGS flags = mtInternal) : BitMap(NULL, 0), _flags(flags) {}
CHeapBitMap() : CHeapBitMap(mtInternal) {}
explicit CHeapBitMap(MEMFLAGS flags) : GrowableBitMap(0, false), _flags(flags) {}
// Clears the bitmap memory.
CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags = mtInternal, bool clear = true);
~CHeapBitMap();
// Resize the backing bitmap memory.
//
// Old bits are transferred to the new memory
// and the extended memory is (optionally) cleared.
void resize(idx_t new_size_in_bits, bool clear = true);
// Set up and (optionally) clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or initialize.
void initialize(idx_t size_in_bits, bool clear = true);
// Set up and (optionally) clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t size_in_bits, bool clear = true);
bm_word_t* allocate(idx_t size_in_words) const;
void free(bm_word_t* map, idx_t size_in_words) const;
};
// Convenience class wrapping BitMap which provides multiple bits per slot.

View File

@ -23,6 +23,7 @@
#include "precompiled.hpp"
#include "logging/logStream.hpp"
#include "memory/arena.hpp"
#include "memory/resourceArea.hpp"
#include "utilities/bitMap.inline.hpp"
#include "unittest.hpp"
@ -83,25 +84,25 @@ class BitMapTest {
EXPECT_TRUE(map.is_same(map2));
}
template <class ReinitializableBitMapClass>
static void testReinitialize(BitMap::idx_t init_size) {
ResourceMark rm;
ResourceBitMap map(init_size);
ReinitializableBitMapClass map(init_size);
map.reinitialize(BITMAP_SIZE);
fillBitMap(map);
ResourceBitMap map2(BITMAP_SIZE);
ReinitializableBitMapClass map2(BITMAP_SIZE);
fillBitMap(map2);
EXPECT_TRUE(map.is_same(map2)) << "With init_size " << init_size;
}
#ifdef ASSERT
template <class PrintableBitMapClass>
static void testPrintOn(BitMap::idx_t size) {
ResourceMark rm;
ResourceBitMap map(size);
PrintableBitMapClass map(size);
if (size > 0) {
map.set_bit(size / 2);
}
@ -113,9 +114,19 @@ class BitMapTest {
#endif
};
// TestArenaBitMap is the shorthand combination of Arena and ArenaBitMap.
// Multiple inheritance guarantees to construct Arena first.
class TestArenaBitMap : private Arena, public ArenaBitMap {
public:
TestArenaBitMap() : TestArenaBitMap(0) {}
TestArenaBitMap(idx_t size_in_bits, bool clear = true) : Arena(mtTest),
ArenaBitMap(static_cast<Arena*>(this), size_in_bits, clear) {}
};
class TestCHeapBitMap : public CHeapBitMap {
public:
TestCHeapBitMap(size_t size = 0) : CHeapBitMap(size, mtTest) {}
};
TEST_VM(BitMap, resize_grow) {
@ -123,6 +134,8 @@ TEST_VM(BitMap, resize_grow) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeGrow<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeGrow<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
}
TEST_VM(BitMap, resize_shrink) {
@ -130,6 +143,8 @@ TEST_VM(BitMap, resize_shrink) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeShrink<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeShrink<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
}
TEST_VM(BitMap, resize_same) {
@ -137,6 +152,8 @@ TEST_VM(BitMap, resize_same) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeSame<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeSame<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
}
// Verify that when growing with clear, all added bits get cleared,
@ -162,20 +179,28 @@ TEST_VM(BitMap, initialize) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testInitialize<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testInitialize<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
}
TEST_VM(BitMap, reinitialize) {
BitMapTest::testReinitialize(0);
BitMapTest::testReinitialize(BitMapTest::BITMAP_SIZE >> 3);
BitMapTest::testReinitialize(BitMapTest::BITMAP_SIZE);
constexpr BitMap::idx_t sizes[] = {0, BitMapTest::BITMAP_SIZE >> 3, BitMapTest::BITMAP_SIZE};
for (auto size : sizes) {
BitMapTest::testReinitialize<ResourceBitMap>(size);
BitMapTest::testReinitialize<TestArenaBitMap>(size);
}
}
#ifdef ASSERT
TEST_VM(BitMap, print_on) {
BitMapTest::testPrintOn(0);
BitMapTest::testPrintOn(BitMapTest::BITMAP_SIZE >> 3);
BitMapTest::testPrintOn(BitMapTest::BITMAP_SIZE);
constexpr BitMap::idx_t sizes[] = {0, BitMapTest::BITMAP_SIZE >> 3, BitMapTest::BITMAP_SIZE};
for (auto size : sizes) {
BitMapTest::testPrintOn<ResourceBitMap>(size);
BitMapTest::testPrintOn<TestArenaBitMap>(size);
}
}
#endif