More Allocator work, fully working CSmallAllocPool

This commit is contained in:
Phillip Stephens 2022-01-15 22:59:00 -08:00
parent 4ba5db133d
commit d9d79a460b
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
9 changed files with 499 additions and 67 deletions

View File

@ -23,24 +23,80 @@ u32 CGameAllocator::GetFreeBinEntryForSize(size_t len) {
}
bool CGameAllocator::Initialize() { return true; }
void* CGameAllocator::Alloc() { return nullptr; }
s32 CGameAllocator::Free(void* ptr) { return 0; }
bool CGameAllocator::Free(void* ptr) { return 0; }
void CGameAllocator::ReleaseAll() {}
void CGameAllocator::AllocSecondary() {}
void CGameAllocator::FreeSecondary() {}
void CGameAllocator::ReleaseAllSecondary() {}
void CGameAllocator::SetOutOfMemoryCallback() {}
void CGameAllocator::EnumAllocations() {}
void CGameAllocator::SetOutOfMemoryCallback(IAllocator::FOutOfMemoryCb cb, void* target) {
x58_oomCallBack = cb;
x5c_oomTarget = target;
}
s32 CGameAllocator::EnumAllocations(IAllocator::FEnumAllocationsCb cb, const void* ptr, bool b) {
s32 ret = 0;
SAllocInfo info;
info.x0_infoPtr = xc_first;
while (true) {
if (info.x0_infoPtr == nullptr) {
return ret;
}
if (static_cast<SGameMemInfo*>(info.x0_infoPtr)->x1c_postGuard != 0xeaeaeaea) {
break;
}
if (static_cast<SGameMemInfo*>(info.x0_infoPtr)->x0_priorGuard != 0xefefefef) {
return -1;
}
void* next = static_cast<SGameMemInfo*>(info.x0_infoPtr)->GetNext();
info.x10_type = static_cast<SGameMemInfo*>(info.x0_infoPtr)->xc_type;
info.x8_hasPrevious = static_cast<SGameMemInfo*>(info.x0_infoPtr)->x10_prev != nullptr;
info.xc_fileAndLne = static_cast<SGameMemInfo*>(info.x0_infoPtr)->x8_fileAndLine;
info.x4_len = static_cast<SGameMemInfo*>(info.x0_infoPtr)->x4_len;
info.x9_ = false;
(*cb)(info, ptr);
++ret;
info.x0_infoPtr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(next) & ~sizeof(SGameMemInfo));
}
return -1;
}
IAllocator::SAllocInfo CGameAllocator::GetAllocInfo(void* ptr) {
SGameMemInfo* info = GetMemInfoFromBlockPtr(ptr);
return {.x0_infoPtr = info,
.x4_len = info->x4_len,
.x8_hasPrevious = info->x10_prev != nullptr,
.x9_ = false,
.xc_fileAndLne = info->x8_line,
.xc_fileAndLne = info->x8_fileAndLine,
.x10_type = info->xc_type};
}
void CGameAllocator::OffsetFakeStatics(s32 offset) { xb8_fakeStatics += offset; }
void CGameAllocator::GetMetrics() {}
IAllocator::SMetrics CGameAllocator::GetMetrics() {
u32 mediumAllocTotalAllocated = x74_mediumPool == nullptr ? 0 : x74_mediumPool->GetTotalEntries();
u32 mediumAllocBlocksAvailable = x74_mediumPool == nullptr ? 0 : x74_mediumPool->GetNumBlocksAvailable();
u32 mediumAllocAllocatedSize =
x74_mediumPool == nullptr ? 0 : x74_mediumPool->GetTotalEntries() - x74_mediumPool->GetNumBlocksAvailable();
u32 mediumAllocNumAllocs = x74_mediumPool == nullptr ? 0 : x74_mediumPool->GetNumAllocs();
u32 smallAllocRemainingSize = 0;
u32 smallAllocAllocatedSize = 0;
u32 smallAllocNumAllocs = 0;
if (x60_smallPool == nullptr) {
} else {
smallAllocRemainingSize = x60_smallPool->GetNumBlocksAvailable();
smallAllocAllocatedSize = x60_smallPool->GetTotalEntries();
smallAllocNumAllocs = x60_smallPool->GetNumAllocs();
}
return SMetrics(x8_heapSize, x80_, x84_, x88_, x8c_, x90_heapSize2, x94_, x98_, x9c_, xa0_, xa4_, smallAllocNumAllocs,
smallAllocAllocatedSize, smallAllocRemainingSize, mediumAllocNumAllocs, mediumAllocAllocatedSize,
mediumAllocBlocksAvailable, x80_ - xb0_, xb4_, xbc_, mediumAllocTotalAllocated, xb8_fakeStatics);
}
} // namespace metaforce

View File

@ -4,76 +4,75 @@
#include <memory>
#include <vector>
#include "Runtime/IAllocator.hpp"
#include "Runtime/RetroTypes.hpp"
#include "Runtime/CSmallAllocPool.hpp"
#include "Runtime/CMediumAllocPool.hpp"
namespace metaforce {
class CCallStack {
const char* x0_line;
const char* x4_type;
public:
CCallStack(int lineNum, const char* lineStr, const char* type) : x0_line(lineStr), x4_type(type) {}
};
enum class EHint {
Unk = (1 << 0),
RoundUpLen = (1 << 1),
};
ENABLE_BITWISE_ENUM(EHint);
enum class EScope {
};
enum class EType {
};
class IAllocator {
public:
struct SAllocInfo {
void* x0_infoPtr;
size_t x4_len;
bool x8_hasPrevious;
bool x9_;
const char* xc_fileAndLne;
const char* x10_type;
};
virtual bool Initialize() = 0; // const COSContext& ctx) = 0;
virtual void* Alloc(size_t size) = 0;
virtual bool Free(void* ptr) = 0;
virtual void ReleaseAll() = 0;
virtual void* AllocSecondary(size_t size) = 0;
virtual bool FreeSecondary(void* ptr) = 0;
virtual void ReleaseAllSecondary() = 0;
virtual void SetOutOfMemoryCallback() = 0;
virtual void EnumAllocations() = 0;
virtual SAllocInfo GetAllocInfo(void* ptr) = 0;
virtual void OffsetFakeStatics(s32 offset) = 0;
virtual void GetMetrics() = 0;
};
class CGameAllocator : public IAllocator {
struct SGameMemInfo {
u32 x0_sentinel = 0xefefefef;
public:
private:
class SGameMemInfo {
public:
u32 x0_priorGuard = 0xefefefef;
size_t x4_len = 0;
const char* x8_line;
const char* x8_fileAndLine;
const char* xc_type;
SGameMemInfo* x10_prev = nullptr;
void* x14_ = nullptr;
void* x10_prev = nullptr;
void* x14_next = nullptr;
void* x18_ = nullptr;
u32 x1c_canary = 0xeaeaeaea;
u32 x1c_postGuard = 0xeaeaeaea;
public:
void* GetPrev() { return x10_prev; }
void SetPrev(void* prev) { x10_prev = prev; }
void* GetNext() { return x14_next; }
void SetNext(void* next) { x14_next = next; }
u32 GetPrevMaskedFlags();
u32 GetNextMaskedFlags();
void SetTopOfHeapAllocated(bool topOfHeap);
};
u8 x4_;
u8 x5_;
u8 x6_;
u8 x7_;
u32 x8_heapSize;
SGameMemInfo* xc_first;
SGameMemInfo* x10_last;
std::array<SGameMemInfo*, 16> x14_bins;
u32 x54_;
FOutOfMemoryCb x58_oomCallBack;
void* x5c_oomTarget;
std::unique_ptr<CSmallAllocPool> x60_smallPool;
void* x64_smallAllocBookKeeping;
void* x68_smallAllocMainData;
bool x6c_;
u32 x70_;
std::unique_ptr<CMediumAllocPool> x74_mediumPool;
void* x78_;
bool x7c_;
u32 x80_;
u32 x84_;
u32 x88_;
u32 x8c_;
u32 x90_heapSize2;
u32 x94_;
u32 x98_;
u32 x9c_;
u32 xa0_;
u32 xa4_;
u32 xa8_;
u32 xac_;
u32 xb0_;
u32 xb4_;
u32 xb8_fakeStatics = 0;
u32 xbc_;
static u32 GetFreeBinEntryForSize(size_t len);
SGameMemInfo* GetMemInfoFromBlockPtr(void* ptr) {
return reinterpret_cast<SGameMemInfo*>(reinterpret_cast<void*>(intptr_t(ptr) - sizeof(SGameMemInfo)));
}
std::array<SGameMemInfo*, 16> x14_bins;
s32 xb8_fakeStatics = 0;
public:
bool Initialize() override; // const COsContext& ctx);
@ -83,10 +82,13 @@ public:
void* AllocSecondary(size_t size) override;
bool FreeSecondary(void* ptr) override;
void ReleaseAllSecondary() override;
void SetOutOfMemoryCallback() override;
void EnumAllocations() override;
void SetOutOfMemoryCallback(FOutOfMemoryCb cb, void* target) override;
s32 EnumAllocations(IAllocator::FEnumAllocationsCb cb, const void* ptr, bool b) override;
SAllocInfo GetAllocInfo(void* ptr) override;
void OffsetFakeStatics(s32 offset) override;
void GetMetrics() override;
SMetrics GetMetrics() override;
void AllocSecondary();
void FreeSecondary();
void* Alloc();
};
} // namespace metaforce

View File

@ -73,6 +73,9 @@ set(RUNTIME_SOURCES_B
ITweak.hpp
IMain.hpp
CStopwatch.hpp
IAllocator.hpp IAllocator.cpp
CSmallAllocPool.hpp CSmallAllocPool.cpp
CMediumAllocPool.hpp CMediumAllocPool.cpp
CGameAllocator.hpp CGameAllocator.cpp
CMemoryCardSys.hpp CMemoryCardSys.cpp
CScannableObjectInfo.hpp CScannableObjectInfo.cpp

View File

@ -0,0 +1,32 @@
#include "CMediumAllocPool.hpp"
namespace metaforce {
//CMediumAllocPool::CMediumAllocPool()
u32 CMediumAllocPool::GetTotalEntries() const {
u32 ret = 0;
for (const auto& puddle : x0_puddles) {
ret += puddle.x1c_numEntries;
}
return ret;
}
u32 CMediumAllocPool::GetNumBlocksAvailable() const {
u32 ret = 0;
for (const auto& puddle : x0_puddles) {
ret += puddle.x14_numBlocks;
}
return ret;
}
u32 CMediumAllocPool::GetNumAllocs() const {
u32 ret = 0;
for (const auto& puddle : x0_puddles) {
ret += puddle.x18_numAllocs;
}
return ret;
}
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <list>
#include <GCNTypes.hpp>
namespace metaforce {
class CMediumAllocPool {
struct SMediumAllocPuddle {
u8 x0_;
void* x4_;
void* x8_;
void* xc_;
u32 x10_;
u32 x14_numBlocks;
u32 x18_numAllocs;
u32 x1c_numEntries;
u8 x20_;
};
std::list<SMediumAllocPuddle> x0_puddles;
std::list<SMediumAllocPuddle>::iterator x18_lastAlloc;
public:
//CMediumAllocPool();
u32 GetTotalEntries() const;
u32 GetNumBlocksAvailable() const;
u32 GetNumAllocs() const;
};
}

131
Runtime/CSmallAllocPool.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "CSmallAllocPool.hpp"
#include <cstring>
namespace metaforce {
CSmallAllocPool::CSmallAllocPool(u32 len, void* mainData, void* bookKeeping)
: x0_mainData(mainData), x4_bookKeeping(bookKeeping), x8_numBlocks(len), x18_numBlocksAvailable(len) {
memset(x4_bookKeeping, 0, len / 2);
}
void* CSmallAllocPool::FindFree(u32 len) {
if (xc_cachedBookKeepingOffset == nullptr) {
xc_cachedBookKeepingOffset = x4_bookKeeping;
}
auto* curKeepingOffset = static_cast<u8*>(xc_cachedBookKeepingOffset);
auto* bookKeepingPtr = static_cast<u8*>(x4_bookKeeping);
auto* bookKeepingEndPtr = bookKeepingPtr + (x8_numBlocks >> 1);
auto* curKeepingIter = curKeepingOffset;
while (true) {
u8* tmpIter = nullptr;
if (static_cast<u8*>(curKeepingIter)[0] == 0 && curKeepingIter != bookKeepingEndPtr) {
tmpIter = curKeepingIter;
do {
++tmpIter;
if (tmpIter == curKeepingOffset || tmpIter == bookKeepingEndPtr || tmpIter == curKeepingIter + len / 2) {
break;
}
} while (static_cast<u8*>(tmpIter)[0] == 0);
if (tmpIter == curKeepingIter + len / 2) {
if (tmpIter == bookKeepingEndPtr) {
xc_cachedBookKeepingOffset = bookKeepingPtr;
} else {
xc_cachedBookKeepingOffset = curKeepingIter;
}
return curKeepingIter;
}
if (tmpIter == curKeepingOffset) {
return nullptr;
}
if (tmpIter == bookKeepingEndPtr) {
tmpIter = bookKeepingPtr;
}
} else {
tmpIter = bookKeepingPtr;
if (curKeepingIter != bookKeepingEndPtr) {
u32 tmp = static_cast<u8*>(curKeepingIter)[0];
tmpIter = curKeepingIter + (tmp >> 5);
}
}
curKeepingIter = tmpIter;
if (tmpIter == curKeepingOffset) {
return nullptr;
}
}
}
void* CSmallAllocPool::Alloc(uint32_t size) {
uint32_t len = 1;
if (size > 3) {
len = (size + 3) / 4;
}
if ((len & 1) != 0) {
len += 1;
}
auto* freePtr = static_cast<uint8_t*>(FindFree(len));
if (freePtr == nullptr) {
return nullptr;
}
auto* bookKeepingStart = static_cast<uint8_t*>(x4_bookKeeping);
uint32_t blockCount = (len - 2) / 2;
auto* bufPtr = static_cast<uint8_t*>(x0_mainData);
static_cast<uint8_t*>(freePtr)[0] = (len << 4) | 0xf;
uint8_t* freePtrIter = freePtr + 1;
if (blockCount != 0) {
uint32_t uVar5 = blockCount >> 3;
if (uVar5 != 0) {
do {
static_cast<uint8_t*>(freePtrIter)[0] = 0xff;
static_cast<uint8_t*>(freePtrIter)[1] = 0xff;
static_cast<uint8_t*>(freePtrIter)[2] = 0xff;
static_cast<uint8_t*>(freePtrIter)[3] = 0xff;
static_cast<uint8_t*>(freePtrIter)[4] = 0xff;
static_cast<uint8_t*>(freePtrIter)[5] = 0xff;
static_cast<uint8_t*>(freePtrIter)[6] = 0xff;
static_cast<uint8_t*>(freePtrIter)[7] = 0xff;
freePtrIter += 8;
} while (--uVar5 != 0u);
blockCount &= 7;
}
if (blockCount != 0) {
do {
static_cast<uint8_t*>(freePtrIter)[0] = 0xff;
++freePtrIter;
} while (--blockCount != 0u);
}
}
x18_numBlocksAvailable = x18_numBlocksAvailable - len;
++x1c_numAllocs;
return bufPtr + ((freePtr - bookKeepingStart) * 8);
}
bool CSmallAllocPool::Free(void* buf) {
u32 offset = static_cast<u8*>(buf) - static_cast<u8*>(x0_mainData);
u32 block = (offset >> 2) + static_cast<u32>(static_cast<s32>(offset < 0 && ((offset & 3) != 0u)) != 0);
u32 numBlocks = (static_cast<u8*>(x4_bookKeeping)[0] + (block >> 1)) >> (~-(block & 1) & 4) & 0xf;
x18_numBlocksAvailable += numBlocks;
--x1c_numAllocs;
x14_ = block;
if (block == x10_) {
x10_ = -1;
}
u8* puVar1 = static_cast<u8*>(x4_bookKeeping) + (block >> 1);
for (; numBlocks != 0; numBlocks -= 2) {
static_cast<u8*>(puVar1)[0] = 0;
++puVar1;
}
return true;
}
} // namespace metaforce

View File

@ -0,0 +1,63 @@
#pragma once
#include "GCNTypes.hpp"
namespace metaforce {
class CSmallAllocPool {
void* x0_mainData;
void* x4_bookKeeping;
uint32_t x8_numBlocks;
void* xc_cachedBookKeepingOffset = nullptr;
int32_t x10_ = -1;
uint32_t x14_ = -1;
uint32_t x18_numBlocksAvailable = 0;
uint32_t x1c_numAllocs = 0;
[[nodiscard]] void* FindFree(uint32_t len);
public:
CSmallAllocPool(uint32_t len, void* mainData, void* bookKeeping);
[[nodiscard]] void* Alloc(uint32_t size);
[[nodiscard]] bool Free(void* buf);
[[nodiscard]] uint32_t GetNumUsedBlocks() const {
uint32_t usedBlocks = 0;
for (uint32_t i = 0; i < x8_numBlocks / 2;) {
uint8_t p = static_cast<uint8_t*>(x4_bookKeeping)[i];
// Don't check the header directly, make sure we extract the block count, as it's actually possible to allocate a
// 0 block buffer, Retro worked around this in retail by not allowing CGameAllocator to allocate buffers >= 0x39
// bytes
if (((p >> 4) & 0xf) != 0) {
uint32_t numBlocks = (p >> 4) & 0xf;
usedBlocks += numBlocks;
// skip over blocks used by this allocation
i += numBlocks / 2;
} else {
++i;
}
}
return usedBlocks;
}
[[nodiscard]] uint32_t GetNumUnusedBlocks() const {
uint32_t unusedBlocks = 0;
for (uint32_t i = 0; i < x8_numBlocks / 2;) {
uint8_t p = static_cast<uint8_t*>(x4_bookKeeping)[i];
// Don't check the header directly, make sure we extract the block count, as it's actually possible to allocate a
// 0 block buffer, Retro worked around this in retail by not allowing CGameAllocator to allocate buffers >= 0x39
// bytes
if (((p >> 4) & 0xf) != 0) {
// skip over blocks used by this allocation
i += ((p >> 4) & 0xf) / 2;
} else {
++i;
unusedBlocks += 2;
}
}
return unusedBlocks;
}
[[nodiscard]] u32 GetNumBlocksAvailable() const { return x18_numBlocksAvailable; }
[[nodiscard]] u32 GetTotalEntries() const { return x8_numBlocks - x18_numBlocksAvailable; }
[[nodiscard]] u32 GetNumAllocs() const { return x1c_numAllocs; }
};
} // namespace metaforce

31
Runtime/IAllocator.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "IAllocator.hpp"
namespace metaforce {
IAllocator::SMetrics::SMetrics(u32 heapSize, u32 unk1, u32 unk2, u32 unk3, u32 unk4, u32 heapSize2, u32 unk5, u32 unk6,
u32 unk7, u32 unk8, u32 unk9, u32 smallAllocNumAllocs, u32 smallAllocAllocatedSize,
u32 smallAllocRemainingSize, u32 mediumAllocNumAllocs, u32 mediumAllocAllocatedSize,
u32 mediumAllocBlocksAvailable, u32 unk10, u32 unk11, u32 unk12,
u32 mediumAllocTotalAllocated, u32 fakeStatics)
: x0_heapSize(heapSize)
, x4_(unk1)
, x8_(unk2)
, xc_(unk3)
, x10_(unk4)
, x14_heapSize2(heapSize2)
, x18_(unk5)
, x1c_(unk6)
, x20_(unk7)
, x24_(unk8)
, x28_(unk9)
, x2c_smallNumAllocs(smallAllocNumAllocs)
, x30_smallAllocatedSize(smallAllocAllocatedSize)
, x34_smallRemainingSize(smallAllocRemainingSize)
, x38_mediumNumAllocs(mediumAllocNumAllocs)
, x3c_mediumAllocatedSize(mediumAllocAllocatedSize)
, x40_mediumBlocksAvailable(mediumAllocBlocksAvailable)
, x44_(unk10)
, x48_(unk11)
, x4c_(unk12)
, x50_mediumTotalAllocated(mediumAllocTotalAllocated)
, x54_fakeStatics(fakeStatics) {}
}

85
Runtime/IAllocator.hpp Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include <athena/Global.hpp>
#include "Runtime/GCNTypes.hpp"
namespace metaforce {
class CCallStack {
const char* x0_line;
const char* x4_type;
public:
CCallStack(int lineNum, const char* lineStr, const char* type) : x0_line(lineStr), x4_type(type) {}
};
enum class EHint {
Unk = (1 << 0),
RoundUpLen = (1 << 1),
};
ENABLE_BITWISE_ENUM(EHint);
enum class EScope {
};
enum class EType {
};
class IAllocator {
public:
using FOutOfMemoryCb = bool (*)(void*, u32);
struct SMetrics {
u32 x0_heapSize;
u32 x4_;
u32 x8_;
u32 xc_;
u32 x10_;
u32 x14_heapSize2; // Remaining heap size?
u32 x18_;
u32 x1c_;
u32 x20_;
u32 x24_;
u32 x28_;
u32 x2c_smallNumAllocs;
u32 x30_smallAllocatedSize;
u32 x34_smallRemainingSize;
u32 x38_mediumNumAllocs;
u32 x3c_mediumAllocatedSize;
u32 x40_mediumBlocksAvailable;
u32 x44_;
u32 x48_;
u32 x4c_;
u32 x50_mediumTotalAllocated;
u32 x54_fakeStatics;
SMetrics(u32 heapSize, u32 unk1, u32 unk2, u32 unk3, u32 unk4, u32 heapSize2, u32 unk5, u32 unk6, u32 unk7,
u32 unk8, u32 unk9, u32 smallAllocNumAllocs, u32 smallAllocAllocatedSize, u32 smallAllocRemainingSize,
u32 mediumAllocNumAllocs, u32 mediumAllocAllocatedSize, u32 mediumAllocBlocksAvailable, u32 unk10, u32 unk11, u32 unk12,
u32 mediumAllocTotalAllocated, u32 fakeStatics);
};
struct SAllocInfo {
void* x0_infoPtr;
size_t x4_len;
bool x8_hasPrevious;
bool x9_;
const char* xc_fileAndLne;
const char* x10_type;
};
using FEnumAllocationsCb = const bool (*)(const SAllocInfo& info, const void* ptr);
virtual bool Initialize() = 0; // const COSContext& ctx) = 0;
virtual void* Alloc(size_t size) = 0;
virtual bool Free(void* ptr) = 0;
virtual void ReleaseAll() = 0;
virtual void* AllocSecondary(size_t size) = 0;
virtual bool FreeSecondary(void* ptr) = 0;
virtual void ReleaseAllSecondary() = 0;
virtual void SetOutOfMemoryCallback(FOutOfMemoryCb cb, void* target) = 0;
virtual s32 EnumAllocations(FEnumAllocationsCb cb, const void* ptr, bool b) = 0;
virtual SAllocInfo GetAllocInfo(void* ptr) = 0;
virtual void OffsetFakeStatics(s32 offset) = 0;
virtual SMetrics GetMetrics() = 0;
};
}