WIP Block allocator implementation
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, macos-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
2026-09-20 16:22:23 -07:00
parent 513db7130b
commit 07bb4a4628
2 changed files with 70 additions and 59 deletions

View File

@@ -78,6 +78,12 @@ private:
// Remove a free block from the index // Remove a free block from the index
void removeFreeBlock(TLSFBlock* block); void removeFreeBlock(TLSFBlock* block);
// Find a free block that can hold at least size bytes
TLSFBlock* findFreeBlock(size_t size) const;
// Get the usable size of a block
size_t getBlockUsableSize(const TLSFBlock* block) const;
}; };
} // namespace mimir } // namespace mimir

View File

@@ -41,7 +41,7 @@ namespace mimir {
// Two-Level Segregated Fit (TLSF): http://www.gii.upv.es/tlsf/files/papers/ecrts04_tlsf.pdf // Two-Level Segregated Fit (TLSF): http://www.gii.upv.es/tlsf/files/papers/ecrts04_tlsf.pdf
const size_t kMinblockSize = 16; const size_t kMinBlockSize = 16;
struct TLSFBlock struct TLSFBlock
{ {
@@ -123,13 +123,13 @@ bool Heap::init(size_t minSize, size_t maxSize)
// Start with one free block, filling the entire Region // Start with one free block, filling the entire Region
TLSFBlock* block = (TLSFBlock*)(m_region.getAddress() + sizeof(TLSFIndex)); TLSFBlock* block = (TLSFBlock*)(m_region.getAddress() + sizeof(TLSFIndex));
block->prevPhys = nullptr;
block->nextFree = nullptr; block->nextFree = nullptr;
block->prevFree = nullptr; block->prevFree = nullptr;
block->size = m_region.getMaxSize() - sizeof(TLSFIndex); block->size = m_region.getMaxSize();
block->size |= 0b11; // T=1: Last Block, F=1: Free Block block->size |= 0b10; // T=1: Last Block, F=1: Free Block
insertFreeBlock(block); insertFreeBlock(block);
return true; return true;
} }
@@ -147,18 +147,26 @@ size_t Heap::getUsed() const
return m_usedSize; return m_usedSize;
} }
size_t Heap::getBlockUsableSize(const TLSFBlock* block) const
{
return (block->size & ~0b11) - 16;
}
void Heap::insertFreeBlock(TLSFBlock* block) void Heap::insertFreeBlock(TLSFBlock* block)
{ {
// Add the block to the index // Add the block to the index
TLSFIndex* index = (TLSFIndex*)m_region.getAddress(); TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
size_t usableSize = (block->size & ~0b11) - 16; size_t usableSize = getBlockUsableSize(block);
size_t firstLevel = std::bit_width(usableSize);
size_t firstLevelIndex = std::bit_width(usableSize) - 5; size_t firstLevelIndex = firstLevel - 5;
size_t secondLevelIndex = (usableSize >> firstLevelIndex) & 0b1111ULL; size_t secondLevelIndex = (usableSize >> firstLevelIndex) & 0b1111ULL;
// Update free bitmaps
index->firstLevelFreeBitMap |= std::bit_floor(usableSize); index->firstLevelFreeBitMap |= std::bit_floor(usableSize);
index->secondLevelFreeBitMap[firstLevelIndex] |= secondLevelIndex & 0b1111ULL; index->secondLevelFreeBitMap[firstLevelIndex] |= secondLevelIndex;
TLSFBlock* prevFirstFreeBlock = index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex]; TLSFBlock* prevFirstFreeBlock = index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex];
index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block; index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block;
@@ -167,6 +175,9 @@ void Heap::insertFreeBlock(TLSFBlock* block)
block->nextFree = prevFirstFreeBlock; block->nextFree = prevFirstFreeBlock;
prevFirstFreeBlock->prevFree = block; prevFirstFreeBlock->prevFree = block;
} }
// Mark block as free
block->size |= 0b01; // T=1: Last Block, F=1: Free Block
} }
void Heap::removeFreeBlock(TLSFBlock* block) void Heap::removeFreeBlock(TLSFBlock* block)
@@ -174,24 +185,25 @@ void Heap::removeFreeBlock(TLSFBlock* block)
// Remove the block from the index // Remove the block from the index
TLSFIndex* index = (TLSFIndex*)m_region.getAddress(); TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
size_t usableSize = (block->size & ~0b11) - 16; size_t usableSize = getBlockUsableSize(block);
size_t firstLevel = std::bit_width(usableSize); size_t firstLevel = std::bit_width(usableSize);
size_t firstLevelIndex = firstLevel - 5; size_t firstLevelIndex = firstLevel - 5;
size_t secondLevelIndex = (usableSize >> (firstLevel - 5)) & 0b1111ULL; size_t secondLevelIndex = (usableSize >> (firstLevelIndex)) & 0b1111ULL;
if (block->prevFree != nullptr) { if (block->prevFree != nullptr) {
// Link the neighboring free blocks together // Link the neighboring free blocks together
block->prevFree = block->nextFree; block->prevFree->nextFree = block->nextFree;
if (block->nextFree) { if (block->nextFree) {
block->nextFree = block->prevFree; block->nextFree->prevFree = block->prevFree;
} }
} else { } else {
// This block was the first for this level // This block was the first for this level
index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block->nextFree; index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block->nextFree;
if (block->nextFree == nullptr) { if (block->nextFree != nullptr) {
// We have removed all the blocks at this level. block->nextFree->prevFree = nullptr;
} else {
// We have removed all the free blocks at this level.
// Update second level bitmask. // Update second level bitmask.
index->secondLevelFreeBitMap[firstLevelIndex] &= ~(1ULL << secondLevelIndex); index->secondLevelFreeBitMap[firstLevelIndex] &= ~(1ULL << secondLevelIndex);
@@ -205,20 +217,15 @@ void Heap::removeFreeBlock(TLSFBlock* block)
} }
} }
if (block->size & 0b10ULL) { // Mark block as non-free
// This block was the last in physical order. block->size &= ~0b01; // T=1: Last Block, F=1: Free Block
if (block->prevPhys) {
// Update the prior block in physical order to mark it as the last block.
block->prevPhys->size &= 0b10ULL;
}
}
} }
// Allocate `size` bytes // Find a free block that can hold at least size bytes
std::byte* Heap::alloc(size_t size) TLSFBlock* Heap::findFreeBlock(size_t size) const
{ {
if (size < kMinblockSize) { if (size < kMinBlockSize) {
size = kMinblockSize; size = kMinBlockSize;
} }
TLSFIndex* index = (TLSFIndex*)m_region.getAddress(); TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
@@ -258,52 +265,50 @@ std::byte* Heap::alloc(size_t size)
return nullptr; return nullptr;
} }
// Any buffer at the second level will fit this allocation. // Any buffer at the second level will fit this allocation. Pick the smallest buffer.
selectedSecondLevel = std::countr_zero(index->secondLevelFreeBitMap[selectedFirstLevel - 4]); selectedSecondLevel = std::countr_zero(index->secondLevelFreeBitMap[selectedFirstLevel - 4]);
} }
// Take the first free block // Take the first free block
TLSFBlock* block = index->secondLevelFreeBlocks[selectedFirstLevel - 4][selectedSecondLevel]; TLSFBlock* block = index->secondLevelFreeBlocks[selectedFirstLevel - 4][selectedSecondLevel];
size_t oldBlockSize = block->size & ~0b11ULL; return block;
bool oldBlockWasLastPhysBlock = (block->size & ~0b10ULL) != 0;
TLSFBlock* prevFreeBlock = block->prevFree;
TLSFBlock* nextFreeBlock = block->nextFree;
TLSFBlock* prevPhysBlock = block->prevPhys;
TLSFBlock* nextPhysBlock = nullptr;
if (!oldBlockWasLastPhysBlock) {
nextPhysBlock = (TLSFBlock*)(((std::byte*)block) + oldBlockSize + 16);
} }
// The next free block at this level replaces this block in the index. // Allocate `size` bytes
index->secondLevelFreeBlocks[selectedFirstLevel - 4][selectedSecondLevel] = nextFreeBlock; std::byte* Heap::alloc(size_t size)
{
if (nextFreeBlock == nullptr) { if (size < kMinBlockSize) {
// This was the last free block at this level. size = kMinBlockSize;
// Update the bitmap for the second level...
index->secondLevelFreeBitMap[selectedFirstLevel - 4] &= ~(1ULL << selectedSecondLevel);
// Check if there are any remaining free blocks within the first level
if (index->secondLevelFreeBitMap[selectedFirstLevel - 4] == 0) {
// This was the last one. Clear the first level bit as well.
index->firstLevelFreeBitMap &= ~(1ULL << selectedFirstLevel);
}
} else {
// The next free block is now the first free block at this level.
nextFreeBlock->prevFree = nullptr;
} }
if (nextPhysBlock) { TLSFBlock* block = findFreeBlock(size);
// Link the next physical block to this one. if (block == nullptr) {
nextPhysBlock->prevPhys = block; // No free block found
} else { return nullptr;
// This is the last physical block, so set the last block bit. }
block->size &= 0b10ULL;
removeFreeBlock(block);
size_t usableSize = getBlockUsableSize(block);
if (size - usableSize >= sizeof(TLSFBlock)) {
// Shrink the block to size; allocate a new free block for the remainder
TLSFBlock* freeBlock = (TLSFBlock*)((std::byte*)block + size + 16ULL);
freeBlock->size = size - usableSize;
freeBlock->prevPhys = block;
freeBlock->prevFree = nullptr;
freeBlock->nextFree = nullptr;
if (block->size & 0b10) { // LSB: T, F: T = Last physical block, F = Free block
// Block was the last physical block
block->size &= ~0b10;
freeBlock->size &= 0b10;
}
block->size -= freeBlock->size & ~0b10;
insertFreeBlock(freeBlock);
} }
block->size = size;
m_usedSize += size; m_usedSize += size;
return (std::byte*)block + 16; return (std::byte*)block + 16;
} }